classes/XLite/Model/Profile.php line 66

Open in your IDE?
  1. <?php
  2. /**
  3.  * Copyright (c) 2011-present Qualiteam software Ltd. All rights reserved.
  4.  * See https://www.x-cart.com/license-agreement.html for license details.
  5.  */
  6. namespace XLite\Model;
  7. use ApiPlatform\Core\Annotation as ApiPlatform;
  8. use ApiPlatform\Core\Bridge\Doctrine\Orm\Filter\SearchFilter;
  9. use Doctrine\ORM\Mapping as ORM;
  10. use XCart\Framework\ApiPlatform\Core\Bridge\Doctrine\Orm\Filter\IntegerDateFilter;
  11. use XLite\API\Endpoint\Profile\DTO\ProfileInput as Input;
  12. use XLite\API\Endpoint\Profile\DTO\ProfileOutput as Output;
  13. use XLite\Core\Translation;
  14. /**
  15.  * The "profile" model class
  16.  *
  17.  * @ORM\Entity
  18.  * @ORM\Table  (name="profiles",
  19.  *      indexes={
  20.  *          @ORM\Index (name="login", columns={"login"}),
  21.  *          @ORM\Index (name="order_id", columns={"order_id"}),
  22.  *          @ORM\Index (name="password", columns={"password"}),
  23.  *          @ORM\Index (name="access_level", columns={"access_level"}),
  24.  *          @ORM\Index (name="first_login", columns={"first_login"}),
  25.  *          @ORM\Index (name="last_login", columns={"last_login"}),
  26.  *          @ORM\Index (name="status", columns={"status"})
  27.  *      }
  28.  * )
  29.  * @ApiPlatform\ApiResource(
  30.  *     input=Input::class,
  31.  *     output=Output::class,
  32.  *     itemOperations={
  33.  *          "get"={
  34.  *              "method"="GET",
  35.  *              "path"="/profiles/{profile_id}.{_format}",
  36.  *              "identifiers"={"profile_id"},
  37.  *              "requirements"={"profile_id"="\d+"}
  38.  *          },
  39.  *          "put"={
  40.  *              "method"="PUT",
  41.  *              "path"="/profiles/{profile_id}.{_format}",
  42.  *              "identifiers"={"profile_id"},
  43.  *              "requirements"={"profile_id"="\d+"}
  44.  *          },
  45.  *          "delete"={
  46.  *              "method"="DELETE",
  47.  *              "path"="/profiles/{profile_id}.{_format}",
  48.  *              "identifiers"={"profile_id"},
  49.  *              "requirements"={"profile_id"="\d+"}
  50.  *          }
  51.  *     },
  52.  *     collectionOperations={
  53.  *          "get"={
  54.  *              "method"="GET",
  55.  *              "path"="/profiles.{_format}",
  56.  *              "identifiers"={"profile_id"}
  57.  *          },
  58.  *          "post"={
  59.  *              "method"="POST",
  60.  *              "path"="/profiles.{_format}",
  61.  *              "identifiers"={"profile_id"},
  62.  *              "controller"="xcart.api.profile.controller"
  63.  *          }
  64.  *     }
  65.  * )
  66.  * @ApiPlatform\ApiFilter(IntegerDateFilter::class, properties={"first_login", "added"})
  67.  * @ApiPlatform\ApiFilter(SearchFilter::class, properties={"login"="partial", "searchFakeField"="partial"})
  68.  */
  69. class Profile extends \XLite\Model\AEntity
  70. {
  71.     /**
  72.      * Status codes
  73.      */
  74.     public const STATUS_ENABLED  'E';
  75.     public const STATUS_DISABLED 'D';
  76.     /**
  77.      * Merge flags
  78.      */
  79.     public const MERGE_ALL       3;
  80.     public const MERGE_ADDRESSES 1;
  81.     public const MERGE_ORDERS    2;
  82.     public const MIN_PASSWORD_LENGTH 8;
  83.     public const MAX_PASSWORD_LENGTH 64;
  84.     /**
  85.      * Profile unique ID
  86.      *
  87.      * @var integer
  88.      *
  89.      * @ORM\Id
  90.      * @ORM\GeneratedValue (strategy="AUTO")
  91.      * @ORM\Column         (type="integer")
  92.      */
  93.     protected $profile_id;
  94.     /**
  95.      * Old Login (e-mail)
  96.      *
  97.      * @var string
  98.      */
  99.     protected $oldLogin '';
  100.     /**
  101.      * Login (e-mail)
  102.      *
  103.      * @var string
  104.      *
  105.      * @ORM\Column (type="string", length=128)
  106.      */
  107.     protected $login '';
  108.     /**
  109.      * Password
  110.      *
  111.      * @var string
  112.      *
  113.      * @ORM\Column (type="string")
  114.      */
  115.     protected $password '';
  116.     /**
  117.      * Password hint
  118.      *
  119.      * @var string
  120.      *
  121.      * @ORM\Column (type="string", length=128)
  122.      */
  123.     protected $password_hint '';
  124.     /**
  125.      * Password hint answer
  126.      *
  127.      * @var string
  128.      *
  129.      * @ORM\Column (type="string", length=128)
  130.      */
  131.     protected $password_hint_answer '';
  132.     /**
  133.      * Password reset key (for 'Forgot password')
  134.      *
  135.      * @var string
  136.      *
  137.      * @ORM\Column (type="string")
  138.      */
  139.     protected $passwordResetKey '';
  140.     /**
  141.      * Timestamp of reset key creation date
  142.      *
  143.      * @var integer
  144.      *
  145.      * @ORM\Column (type="integer")
  146.      */
  147.     protected $passwordResetKeyDate 0;
  148.     /**
  149.      * Access level
  150.      *
  151.      * @var integer
  152.      *
  153.      * @ORM\Column (type="integer")
  154.      */
  155.     protected $access_level 0;
  156.     /**
  157.      * Timestamp of profile creation date
  158.      *
  159.      * @var integer
  160.      *
  161.      * @ORM\Column (type="integer")
  162.      */
  163.     protected $added 0;
  164.     /**
  165.      * Timestamp of first login event
  166.      *
  167.      * @var integer
  168.      *
  169.      * @ORM\Column (type="integer")
  170.      */
  171.     protected $first_login 0;
  172.     /**
  173.      * Timestamp of last login event
  174.      *
  175.      * @var integer
  176.      *
  177.      * @ORM\Column (type="integer")
  178.      */
  179.     protected $last_login 0;
  180.     /**
  181.      * Profile status
  182.      *
  183.      * @var string
  184.      *
  185.      * @ORM\Column (type="string", options={ "fixed": true }, length=1)
  186.      */
  187.     protected $status self::STATUS_ENABLED;
  188.     /**
  189.      * Status comment (reason)
  190.      *
  191.      * @var string
  192.      *
  193.      * @ORM\Column (type="string", length=255)
  194.      */
  195.     protected $statusComment '';
  196.     /**
  197.      * Referer
  198.      *
  199.      * @var string
  200.      *
  201.      * @ORM\Column (type="string", length=255)
  202.      */
  203.     protected $referer '';
  204.     /**
  205.      * Relation to a order
  206.      *
  207.      * @var \XLite\Model\Order
  208.      *
  209.      * @ORM\OneToOne   (targetEntity="XLite\Model\Order")
  210.      * @ORM\JoinColumn (name="order_id", referencedColumnName="order_id", onDelete="CASCADE")
  211.      */
  212.     protected $order;
  213.     /**
  214.      * Relation to an event
  215.      *
  216.      * @var \XLite\Model\OrderHistoryEvents
  217.      *
  218.      * @ORM\OneToMany   (targetEntity="XLite\Model\OrderHistoryEvents", mappedBy="author")
  219.      * @ORM\JoinColumn (name="event_id", referencedColumnName="event_id", onDelete="CASCADE")
  220.      */
  221.     protected $event;
  222.     /**
  223.      * Language code
  224.      *
  225.      * @var string
  226.      *
  227.      * @ORM\Column (type="string", length=2)
  228.      */
  229.     protected $language '';
  230.     /**
  231.      * Last selected shipping id
  232.      *
  233.      * @var integer
  234.      *
  235.      * @ORM\Column (type="integer", nullable=true)
  236.      */
  237.     protected $last_shipping_id;
  238.     /**
  239.      * Last selected payment id
  240.      *
  241.      * @var integer
  242.      *
  243.      * @ORM\Column (type="integer", nullable=true)
  244.      */
  245.     protected $last_payment_id;
  246.     /**
  247.      * Membership: many-to-one relation with memberships table
  248.      *
  249.      * @var \Doctrine\Common\Collections\ArrayCollection
  250.      *
  251.      * @ORM\ManyToOne  (targetEntity="XLite\Model\Membership")
  252.      * @ORM\JoinColumn (name="membership_id", referencedColumnName="membership_id", onDelete="SET NULL")
  253.      */
  254.     protected $membership;
  255.     /**
  256.      * Pending membership: many-to-one relation with memberships table
  257.      *
  258.      * @var \Doctrine\Common\Collections\ArrayCollection
  259.      *
  260.      * @ORM\ManyToOne  (targetEntity="XLite\Model\Membership")
  261.      * @ORM\JoinColumn (name="pending_membership_id", referencedColumnName="membership_id", onDelete="SET NULL")
  262.      */
  263.     protected $pending_membership;
  264.     /**
  265.      * Address book: one-to-many relation with address book entity
  266.      *
  267.      * @var \Doctrine\Common\Collections\ArrayCollection
  268.      *
  269.      * @ORM\OneToMany (targetEntity="XLite\Model\Address", mappedBy="profile", cascade={"all"})
  270.      */
  271.     protected $addresses;
  272.     /**
  273.      * Roles
  274.      *
  275.      * @var \Doctrine\Common\Collections\Collection
  276.      *
  277.      * @ORM\ManyToMany (targetEntity="XLite\Model\Role", mappedBy="profiles", cascade={"merge","detach","persist"})
  278.      */
  279.     protected $roles;
  280.     /**
  281.      * The count of orders placed by the user
  282.      *
  283.      * @var integer
  284.      */
  285.     protected $orders_count null;
  286.     /**
  287.      * Flag of anonymous profile (used for checkout process only)
  288.      *
  289.      * @var boolean
  290.      *
  291.      * @ORM\Column (type="boolean")
  292.      */
  293.     protected $anonymous false;
  294.     /**
  295.      * Flag if the user needs to change the password.
  296.      * The customers only
  297.      *
  298.      * @var boolean
  299.      *
  300.      * @ORM\Column (type="boolean")
  301.      */
  302.     protected $forceChangePassword false;
  303.     /**
  304.      * Date of last login attempt
  305.      *
  306.      * @var integer
  307.      *
  308.      * @ORM\Column (type="integer", options={ "unsigned": true })
  309.      */
  310.     protected $dateOfLoginAttempt 0;
  311.     /**
  312.      * Count of login attempt
  313.      *
  314.      * @var integer
  315.      *
  316.      * @ORM\Column (type="integer")
  317.      */
  318.     protected $countOfLoginAttempts 0;
  319.     /**
  320.      * Fake field for search
  321.      *
  322.      * @var string
  323.      *
  324.      * @ORM\Column (type="text", nullable=true)
  325.      */
  326.     protected $searchFakeField;
  327.     /**
  328.      * Flag to exporting entities
  329.      *
  330.      * @var boolean
  331.      *
  332.      * @ORM\Column (type="boolean")
  333.      */
  334.     protected $xcPendingExport false;
  335.     /**
  336.      * Checkout email
  337.      *
  338.      * @var string
  339.      *
  340.      * @ORM\Column (type="string")
  341.      */
  342.     protected $lastCheckoutEmail '';
  343.     /**
  344.      * Set referer
  345.      *
  346.      * @param string $value Value
  347.      *
  348.      * @return void
  349.      */
  350.     public function setReferer($value)
  351.     {
  352.         $this->referer substr($value0255);
  353.     }
  354.     /**
  355.      * Set membership
  356.      *
  357.      * @param \XLite\Model\Membership $membership Membership OPTIONAL
  358.      *
  359.      * @return void
  360.      */
  361.     public function setMembership(\XLite\Model\Membership $membership null)
  362.     {
  363.         $this->membership $membership;
  364.     }
  365.     /**
  366.      * Set pending membership
  367.      *
  368.      * @param \XLite\Model\Membership $pendingMembership Pending membership OPTIONAL
  369.      *
  370.      * @return void
  371.      */
  372.     public function setPendingMembership(\XLite\Model\Membership $pendingMembership null)
  373.     {
  374.         $this->pending_membership $pendingMembership;
  375.     }
  376.     /**
  377.      * Get membership Id
  378.      *
  379.      * @return integer
  380.      */
  381.     public function getMembershipId()
  382.     {
  383.         return $this->getMembership() ? $this->getMembership()->getMembershipId() : null;
  384.     }
  385.     /**
  386.      * Get pending membership Id
  387.      *
  388.      * @return integer
  389.      */
  390.     public function getPendingMembershipId()
  391.     {
  392.         return $this->getPendingMembership() ? $this->getPendingMembership()->getMembershipId() : null;
  393.     }
  394.     /**
  395.      * Get name
  396.      *
  397.      * @param boolean $useDefault Administrator|Customer if missing OPTIONAL
  398.      * @param boolean $shortName FirstName only OPTIONAL
  399.      *
  400.      * @return string
  401.      */
  402.     public function getName($useDefault true$shortName false)
  403.     {
  404.         $address $this->getBillingAddress() ?: $this->getShippingAddress();
  405.         if ($address) {
  406.             if ($shortName && $address->getFirstname()) {
  407.                 return trim($address->getFirstname());
  408.             } elseif ($address->getFirstname() || $address->getLastname()) {
  409.                 return trim($address->getFirstname() . ' ' $address->getLastname());
  410.             }
  411.         }
  412.         return $this->getNameFromDefaultAddress() ? $this->getNameFromDefaultAddress() : ($useDefault $this->getDefaultName() : '');
  413.     }
  414.     /**
  415.      * Get default name from address
  416.      *
  417.      * @return string
  418.      */
  419.     public function getNameFromDefaultAddress()
  420.     {
  421.         if (count($this->getAddresses())) {
  422.             foreach ($this->getAddresses() as $address) {
  423.                 $result trim($address->getFirstname() . ' ' $address->getLastname());
  424.                 break;
  425.             }
  426.         } else {
  427.             $result false;
  428.         }
  429.         return $result;
  430.     }
  431.     /**
  432.      * Get default name
  433.      *
  434.      * @return string
  435.      */
  436.     protected function getDefaultName()
  437.     {
  438.         return $this->isAdmin()
  439.             ? static::t('na_admin')
  440.             : static::t('na_customer');
  441.     }
  442.     /**
  443.      * Returns billing address
  444.      *
  445.      * @return \XLite\Model\Address
  446.      */
  447.     public function getBillingAddress()
  448.     {
  449.         return $this->getAddressByType(\XLite\Model\Address::BILLING);
  450.     }
  451.     /**
  452.      * Returns shipping address
  453.      *
  454.      * @return \XLite\Model\Address
  455.      */
  456.     public function getShippingAddress()
  457.     {
  458.         return $this->getAddressByType(\XLite\Model\Address::SHIPPING);
  459.     }
  460.     /**
  461.      * Switches current billing address to a new one
  462.      *
  463.      * @param \XLite\Model\Address $new
  464.      */
  465.     public function setBillingAddress($new)
  466.     {
  467.         $current $this->getBillingAddress();
  468.         if ($current && $current->getUniqueIdentifier() == $new->getUniqueIdentifier()) {
  469.             return;
  470.         }
  471.         $this->setAddress('billing'$new);
  472.     }
  473.     /**
  474.      * Switches current shipping address to a new one
  475.      *
  476.      * @param \XLite\Model\Address $new
  477.      */
  478.     public function setShippingAddress($new)
  479.     {
  480.         $current $this->getShippingAddress();
  481.         if ($current && $current->getUniqueIdentifier() == $new->getUniqueIdentifier()) {
  482.             return;
  483.         }
  484.         $this->setAddress('shipping'$new);
  485.     }
  486.     /**
  487.      * Set current address by type
  488.      *
  489.      * @param string $type
  490.      * @param \XLite\Model\Address $new
  491.      */
  492.     protected function setAddress($type$new)
  493.     {
  494.         $current = ($type == 'shipping')
  495.             ? $this->getShippingAddress()
  496.             : $this->getBillingAddress();
  497.         if ($current && $current->getUniqueIdentifier() == $new->getUniqueIdentifier()) {
  498.             return;
  499.         }
  500.         $useAsOtherType \XLite\Core\Session::getInstance()->same_address ?? null;
  501.         // Disable current address
  502.         if ($current) {
  503.             if ($current->getIsWork()) {
  504.                 $this->getAddresses()->removeElement($current);
  505.                 \XLite\Core\Database::getEM()->remove($current);
  506.             }
  507.             $useAsOtherType = ($type == 'shipping')
  508.                 ? $current->getIsBilling()
  509.                 : $current->getIsShipping();
  510.             $current->setIsShipping(false);
  511.             $current->setIsBilling(false);
  512.         }
  513.         // Check if new address is not assigned to this profile
  514.         $addToProfile true;
  515.         foreach ($this->getAddresses() as $profileAddress) {
  516.             if ($profileAddress->getUniqueIdentifier() == $new->getUniqueIdentifier()) {
  517.                 $addToProfile false;
  518.             }
  519.         }
  520.         if ($addToProfile) {
  521.             $this->addAddresses($new);
  522.             $new->setProfile($this);
  523.         }
  524.         if ($type == 'shipping') {
  525.             $new->setIsShipping(true);
  526.             if ($useAsOtherType !== null && $current && !$current->getIsWork()) {
  527.                 $new->setIsBilling($useAsOtherType);
  528.             }
  529.         } else {
  530.             $new->setIsBilling(true);
  531.             if ($useAsOtherType !== null && $current && !$current->getIsWork()) {
  532.                 $new->setIsShipping($useAsOtherType);
  533.             }
  534.         }
  535.     }
  536.     /**
  537.      * Returns first available address
  538.      *
  539.      * @return \XLite\Model\Address
  540.      */
  541.     public function getFirstAddress()
  542.     {
  543.         $result null;
  544.         foreach ($this->getAddresses() as $address) {
  545.             $result $address;
  546.             break;
  547.         }
  548.         return $result;
  549.     }
  550.     /**
  551.      * Has tax exemption
  552.      *
  553.      * @return boolean
  554.      */
  555.     public function hasTaxExemption()
  556.     {
  557.         return false;
  558.     }
  559.     /**
  560.      * Returns the number of orders places by the user
  561.      *
  562.      * @return integer
  563.      */
  564.     public function getOrdersCount()
  565.     {
  566.         if ($this->orders_count === null) {
  567.             $cnd = new \XLite\Core\CommonCell();
  568.             $cnd->profile $this;
  569.             $this->orders_count \XLite\Core\Database::getRepo('XLite\Model\Order')->search($cndtrue);
  570.         }
  571.         return $this->orders_count;
  572.     }
  573.     /**
  574.      * Check if profile is enabled
  575.      *
  576.      * @return boolean
  577.      */
  578.     public function isEnabled()
  579.     {
  580.         return strtoupper($this->getStatus()) === static::STATUS_ENABLED;
  581.     }
  582.     /**
  583.      * Check if profile is disabled
  584.      *
  585.      * @return bool
  586.      */
  587.     public function isDisabled(): bool
  588.     {
  589.         return strtoupper($this->getStatus()) === static::STATUS_DISABLED;
  590.     }
  591.     /**
  592.      * Enable user profile
  593.      *
  594.      * @return void
  595.      */
  596.     public function enable()
  597.     {
  598.         $this->setStatus(static::STATUS_ENABLED);
  599.     }
  600.     /**
  601.      * Disable user profile
  602.      *
  603.      * @return void
  604.      */
  605.     public function disable()
  606.     {
  607.         $this->setStatus(static::STATUS_DISABLED);
  608.     }
  609.     /**
  610.      * Returns true if profile has an administrator access level
  611.      *
  612.      * @return boolean
  613.      */
  614.     public function isAdmin()
  615.     {
  616.         return $this->getAccessLevel() >= \XLite\Core\Auth::getInstance()->getAdminAccessLevel();
  617.     }
  618.     /**
  619.      * Create an entity profile in the database
  620.      *
  621.      * @return boolean
  622.      */
  623.     public function create()
  624.     {
  625.         $this->prepareCreate();
  626.         return parent::create();
  627.     }
  628.     /**
  629.      * Update an entity in the database
  630.      *
  631.      * @param boolean $cloneMode Clone mode OPTIONAL
  632.      *
  633.      * @return boolean
  634.      */
  635.     public function update($cloneMode false)
  636.     {
  637.         // Check if user with specified e-mail address is already exists
  638.         $exists $cloneMode
  639.             null
  640.             \XLite\Core\Database::getRepo('XLite\Model\Profile')->checkRegisteredUserWithSameLogin($this);
  641.         if ($exists) {
  642.             $this->addErrorEmailExists();
  643.             $result false;
  644.         } else {
  645.             $this->updateSearchFakeField();
  646.             // Do an entity update
  647.             $result parent::update();
  648.         }
  649.         return $result;
  650.     }
  651.     /**
  652.      * Delete an entity profile from the database
  653.      *
  654.      * @return boolean
  655.      */
  656.     public function delete()
  657.     {
  658.         // Check if the deleted profile is a last admin profile
  659.         if ($this->isAdmin() && \XLite\Core\Database::getRepo('XLite\Model\Profile')->findCountOfAdminAccounts() == 1) {
  660.             $result false;
  661.             \XLite\Core\TopMessage::addError('The only remaining active administrator profile cannot be deleted.');
  662.         } else {
  663.             $result parent::delete();
  664.         }
  665.         return $result;
  666.     }
  667.     /**
  668.      * Check if billing and shipping addresses are equal or not
  669.      * TODO: review method after implementing at one-step-checkout
  670.      *
  671.      * @return boolean
  672.      */
  673.     public function isSameAddress()
  674.     {
  675.         $result false;
  676.         $billingAddress $this->getBillingAddress();
  677.         $shippingAddress $this->getShippingAddress();
  678.         if ($billingAddress !== null && $shippingAddress !== null) {
  679.             $result true;
  680.             if ($billingAddress->getAddressId() != $shippingAddress->getAddressId()) {
  681.                 $addressFields $billingAddress->getAvailableAddressFields();
  682.                 foreach ($addressFields as $name) {
  683.                     $methodName 'get' \Includes\Utils\Converter::convertToUpperCamelCase($name);
  684.                     // Compare field values of billing and shipping addresses
  685.                     if ($billingAddress->$methodName() != $shippingAddress->$methodName()) {
  686.                         $result false;
  687.                         break;
  688.                     }
  689.                 }
  690.             }
  691.         }
  692.         return $result;
  693.     }
  694.     /**
  695.      * Check - billing and shipping addresses are equal or not
  696.      *
  697.      * @param boolean $strict Flag: true - both billing and shipping addresses must be defined OPTIONAL
  698.      *
  699.      * @return boolean
  700.      */
  701.     public function isEqualAddress($strict false)
  702.     {
  703.         $billingAddress $this->getBillingAddress();
  704.         $shippingAddress $this->getShippingAddress();
  705.         $result $billingAddress !== null && $shippingAddress !== null;
  706.         return $strict
  707.             $result && $billingAddress->getAddressId() == $shippingAddress->getAddressId()
  708.             : !$result || $billingAddress->getAddressId() == $shippingAddress->getAddressId();
  709.     }
  710.     /**
  711.      * Clone
  712.      *
  713.      * @return \XLite\Model\Profile
  714.      */
  715.     public function cloneEntity()
  716.     {
  717.         $newProfile parent::cloneEntity();
  718.         $newProfile->setMembership($this->getMembership());
  719.         $newProfile->setPendingMembership($this->getPendingMembership());
  720.         $newProfile->setPassword('');
  721.         $billingAddress $this->getBillingAddress();
  722.         if ($billingAddress !== null) {
  723.             $newBillingAddress $billingAddress->cloneEntity();
  724.             $newBillingAddress->setProfile($newProfile);
  725.             $newProfile->addAddresses($newBillingAddress);
  726.         }
  727.         $shippingAddress $this->getShippingAddress();
  728.         if (
  729.             $shippingAddress
  730.             && (!$billingAddress
  731.                 || $billingAddress->getAddressId() != $shippingAddress->getAddressId()
  732.                 || $billingAddress->getAddressId() === null
  733.                 || $shippingAddress->getAddressId() === null
  734.             )
  735.         ) {
  736.             $newShippingAddress $shippingAddress->cloneEntity();
  737.             $newShippingAddress->setProfile($newProfile);
  738.             $newProfile->addAddresses($newShippingAddress);
  739.         }
  740.         return $newProfile;
  741.     }
  742.     /**
  743.      * Constructor
  744.      *
  745.      * @param array $data Entity properties OPTIONAL
  746.      */
  747.     public function __construct(array $data = [])
  748.     {
  749.         $this->addresses = new \Doctrine\Common\Collections\ArrayCollection();
  750.         $this->roles     = new \Doctrine\Common\Collections\ArrayCollection();
  751.         parent::__construct($data);
  752.     }
  753.     /**
  754.      * Get language code
  755.      *
  756.      * @param boolean $isCreateMode Flag to get original entity language OPTIONAL
  757.      *
  758.      * @return string
  759.      */
  760.     public function getLanguage($isCreateMode false)
  761.     {
  762.         return $isCreateMode
  763.             $this->getLanguageForCreateProfile()
  764.             : $this->checkForActiveLanguage($this->language);
  765.     }
  766.     /**
  767.      * Define the language code for created profile
  768.      *
  769.      * @return string
  770.      */
  771.     protected function getLanguageForCreateProfile()
  772.     {
  773.         return $this->language;
  774.     }
  775.     /**
  776.      * Check if the language code is in the active languages list
  777.      * If customer language is not used right now, the default customer language code is used
  778.      *
  779.      * @param string $languageCode Language code
  780.      *
  781.      * @return string
  782.      */
  783.     protected function checkForActiveLanguage($languageCode)
  784.     {
  785.         $result $languageCode;
  786.         $langs \XLite\Core\Database::getRepo('XLite\Model\Language')->findActiveLanguages();
  787.         if (!empty($langs)) {
  788.             $resultModel \Includes\Utils\ArrayManager::searchInObjectsArray(
  789.                 $langs,
  790.                 'getCode',
  791.                 $result
  792.             );
  793.             if ($resultModel === null) {
  794.                 $result \XLite\Core\Config::getInstance()->General->default_language;
  795.             }
  796.         }
  797.         return $result;
  798.     }
  799.     /**
  800.      * Set order
  801.      *
  802.      * @param \XLite\Model\Order $order Order OPTIONAL
  803.      *
  804.      * @return void
  805.      */
  806.     public function setOrder(\XLite\Model\Order $order null)
  807.     {
  808.         $this->order $order;
  809.     }
  810.     /**
  811.      * Get password hash algorithm
  812.      *
  813.      * @return string
  814.      */
  815.     public function getPasswordAlgo()
  816.     {
  817.         $parts explode(':'$this->getPassword(), 2);
  818.         return count($parts) === 'MD5' $parts[0];
  819.     }
  820.     /**
  821.      * Merge profile with another profile
  822.      *
  823.      * @param \XLite\Model\Profile $profile Profile
  824.      * @param integer              $flag    Peration flag OPTIONAL
  825.      *
  826.      * @return integer
  827.      */
  828.     public function mergeWithProfile(\XLite\Model\Profile $profile$flag self::MERGE_ALL)
  829.     {
  830.         $result 0;
  831.         // Addresses
  832.         if ($flag & static::MERGE_ADDRESSES) {
  833.             foreach ($profile->getAddresses() as $address) {
  834.                 $found false;
  835.                 foreach ($this->getAddresses() as $a) {
  836.                     if ($a->isEqualAddress($address)) {
  837.                         $found true;
  838.                         break;
  839.                     }
  840.                 }
  841.                 if (!$found) {
  842.                     $address $address->cloneEntity();
  843.                     $this->addAddresses($address);
  844.                     $address->setProfile($this);
  845.                 }
  846.             }
  847.             $result |= static::MERGE_ADDRESSES;
  848.         }
  849.         // Orders
  850.         if ($flag & static::MERGE_ORDERS) {
  851.             $cnd = new \XLite\Core\CommonCell();
  852.             $cnd->profile $profile;
  853.             foreach (\XLite\Core\Database::getRepo('XLite\Model\Order')->search($cnd) as $order) {
  854.                 $order->setOrigProfile($this);
  855.             }
  856.             $result |= static::MERGE_ORDERS;
  857.         }
  858.         return $result;
  859.     }
  860.     /**
  861.      * Prepare object for its creation in the database
  862.      *
  863.      * @return void
  864.      */
  865.     protected function prepareCreate()
  866.     {
  867.     }
  868.     /**
  869.      * Update field for search optimization
  870.      *
  871.      * @return void
  872.      */
  873.     public function updateSearchFakeField()
  874.     {
  875.         $searchFakeFieldParts = [];
  876.         foreach ($this->getAddresses() as $address) {
  877.             $searchFakeFieldParts[] = trim($address->getFirstname() . ' ' $address->getLastname() . ' ' $address->getFirstname());
  878.         }
  879.         $searchFakeFieldParts[] = $this->getLogin();
  880.         $this->setSearchFakeField(implode(';'$searchFakeFieldParts));
  881.     }
  882.     /**
  883.      * Returns address by its type (shipping or billing)
  884.      *
  885.      * @param string $atype Address type: b - billing, s - shipping OPTIONAL
  886.      *
  887.      * @return \XLite\Model\Address
  888.      */
  889.     protected function getAddressByType($atype \XLite\Model\Address::BILLING)
  890.     {
  891.         $result null;
  892.         foreach ($this->getAddresses() ?: [] as $address) {
  893.             if (
  894.                 ($atype === \XLite\Model\Address::BILLING && $address->getIsBilling())
  895.                 || ($atype === \XLite\Model\Address::SHIPPING && $address->getIsShipping())
  896.             ) {
  897.                 // Select address if its type is same as a requested type...
  898.                 $result $address;
  899.                 break;
  900.             }
  901.         }
  902.         return $result;
  903.     }
  904.     /**
  905.      * Add error top message 'Email already exists...'
  906.      *
  907.      * @return void
  908.      */
  909.     protected function addErrorEmailExists()
  910.     {
  911.         \XLite\Core\TopMessage::addError('This e-mail address is already in use by another user.');
  912.     }
  913.     // {{{ Roles
  914.     /**
  915.      * Check - specified permission is allowed or not
  916.      *
  917.      * @param string $code Permission code
  918.      *
  919.      * @return boolean
  920.      */
  921.     public function isPermissionAllowed($code)
  922.     {
  923.         $result false;
  924.         if (count($this->getRoles())) {
  925.             foreach ($this->getRoles() as $role) {
  926.                 if ($role->isPermissionAllowed($code)) {
  927.                     $result true;
  928.                     break;
  929.                 }
  930.             }
  931.         } elseif (\XLite\Core\Database::getRepo('XLite\Model\Role')->count() === 0) {
  932.             $result true;
  933.         }
  934.         return $result;
  935.     }
  936.     // }}}
  937.     /**
  938.      * Get profile_id
  939.      *
  940.      * @return integer
  941.      */
  942.     public function getProfileId(): ?int
  943.     {
  944.         return $this->profile_id;
  945.     }
  946.     public function setProfileId(int $profileId): static
  947.     {
  948.         $this->profile_id $profileId;
  949.         return $this;
  950.     }
  951.     /**
  952.      * Set login
  953.      *
  954.      * @param string $login
  955.      * @return Profile
  956.      */
  957.     public function setLogin($login)
  958.     {
  959.         $this->oldLogin $this->login;
  960.         $this->login $login;
  961.         return $this;
  962.     }
  963.     /**
  964.      * Get oldLogin
  965.      *
  966.      * @return string
  967.      */
  968.     public function getOldLogin()
  969.     {
  970.         return $this->oldLogin;
  971.     }
  972.     /**
  973.      * Get login
  974.      *
  975.      * @return string
  976.      */
  977.     public function getLogin(): string
  978.     {
  979.         return $this->login;
  980.     }
  981.     /**
  982.      * Set password
  983.      *
  984.      * @param string $password
  985.      * @return Profile
  986.      */
  987.     public function setPassword($password)
  988.     {
  989.         $this->password $password;
  990.         return $this;
  991.     }
  992.     /**
  993.      * Get password
  994.      *
  995.      * @return string
  996.      */
  997.     public function getPassword()
  998.     {
  999.         return $this->password;
  1000.     }
  1001.     /**
  1002.      * Set password_hint
  1003.      *
  1004.      * @param string $passwordHint
  1005.      * @return Profile
  1006.      */
  1007.     public function setPasswordHint($passwordHint)
  1008.     {
  1009.         $this->password_hint $passwordHint;
  1010.         return $this;
  1011.     }
  1012.     /**
  1013.      * Get password_hint
  1014.      *
  1015.      * @return string
  1016.      */
  1017.     public function getPasswordHint()
  1018.     {
  1019.         return $this->password_hint;
  1020.     }
  1021.     /**
  1022.      * Set password_hint_answer
  1023.      *
  1024.      * @param string $passwordHintAnswer
  1025.      * @return Profile
  1026.      */
  1027.     public function setPasswordHintAnswer($passwordHintAnswer)
  1028.     {
  1029.         $this->password_hint_answer $passwordHintAnswer;
  1030.         return $this;
  1031.     }
  1032.     /**
  1033.      * Get password_hint_answer
  1034.      *
  1035.      * @return string
  1036.      */
  1037.     public function getPasswordHintAnswer()
  1038.     {
  1039.         return $this->password_hint_answer;
  1040.     }
  1041.     /**
  1042.      * Set passwordResetKey
  1043.      *
  1044.      * @param string $passwordResetKey
  1045.      * @return Profile
  1046.      */
  1047.     public function setPasswordResetKey($passwordResetKey)
  1048.     {
  1049.         $this->passwordResetKey $passwordResetKey;
  1050.         return $this;
  1051.     }
  1052.     /**
  1053.      * Get passwordResetKey
  1054.      *
  1055.      * @return string
  1056.      */
  1057.     public function getPasswordResetKey()
  1058.     {
  1059.         return $this->passwordResetKey;
  1060.     }
  1061.     /**
  1062.      * Set passwordResetKeyDate
  1063.      *
  1064.      * @param integer $passwordResetKeyDate
  1065.      * @return Profile
  1066.      */
  1067.     public function setPasswordResetKeyDate($passwordResetKeyDate)
  1068.     {
  1069.         $this->passwordResetKeyDate $passwordResetKeyDate;
  1070.         return $this;
  1071.     }
  1072.     /**
  1073.      * Get passwordResetKeyDate
  1074.      *
  1075.      * @return integer
  1076.      */
  1077.     public function getPasswordResetKeyDate()
  1078.     {
  1079.         return $this->passwordResetKeyDate;
  1080.     }
  1081.     /**
  1082.      * Set access_level
  1083.      *
  1084.      * @param integer $accessLevel
  1085.      * @return Profile
  1086.      */
  1087.     public function setAccessLevel($accessLevel)
  1088.     {
  1089.         $this->access_level $accessLevel;
  1090.         return $this;
  1091.     }
  1092.     /**
  1093.      * Get access_level
  1094.      *
  1095.      * @return integer
  1096.      */
  1097.     public function getAccessLevel()
  1098.     {
  1099.         return $this->access_level;
  1100.     }
  1101.     /**
  1102.      * Set added
  1103.      *
  1104.      * @param integer $added
  1105.      * @return Profile
  1106.      */
  1107.     public function setAdded($added)
  1108.     {
  1109.         $this->added $added;
  1110.         return $this;
  1111.     }
  1112.     /**
  1113.      * Get added
  1114.      *
  1115.      * @return integer
  1116.      */
  1117.     public function getAdded()
  1118.     {
  1119.         return $this->added;
  1120.     }
  1121.     /**
  1122.      * Set first_login
  1123.      *
  1124.      * @param integer $firstLogin
  1125.      * @return Profile
  1126.      */
  1127.     public function setFirstLogin($firstLogin)
  1128.     {
  1129.         $this->first_login $firstLogin;
  1130.         return $this;
  1131.     }
  1132.     /**
  1133.      * Get first_login
  1134.      *
  1135.      * @return integer
  1136.      */
  1137.     public function getFirstLogin()
  1138.     {
  1139.         return $this->first_login;
  1140.     }
  1141.     /**
  1142.      * Set last_login
  1143.      *
  1144.      * @param integer $lastLogin
  1145.      * @return Profile
  1146.      */
  1147.     public function setLastLogin($lastLogin)
  1148.     {
  1149.         $this->last_login $lastLogin;
  1150.         return $this;
  1151.     }
  1152.     /**
  1153.      * Get last_login
  1154.      *
  1155.      * @return integer
  1156.      */
  1157.     public function getLastLogin()
  1158.     {
  1159.         return $this->last_login;
  1160.     }
  1161.     /**
  1162.      * Set status
  1163.      *
  1164.      * @param string $status
  1165.      * @return Profile
  1166.      */
  1167.     public function setStatus($status)
  1168.     {
  1169.         $this->status $status;
  1170.         return $this;
  1171.     }
  1172.     /**
  1173.      * Get status
  1174.      *
  1175.      * @return string
  1176.      */
  1177.     public function getStatus()
  1178.     {
  1179.         return $this->status;
  1180.     }
  1181.     /**
  1182.      * Set statusComment
  1183.      *
  1184.      * @param string $statusComment
  1185.      * @return Profile
  1186.      */
  1187.     public function setStatusComment($statusComment)
  1188.     {
  1189.         $this->statusComment $statusComment;
  1190.         return $this;
  1191.     }
  1192.     /**
  1193.      * Get statusComment
  1194.      *
  1195.      * @return string
  1196.      */
  1197.     public function getStatusComment()
  1198.     {
  1199.         return $this->statusComment;
  1200.     }
  1201.     /**
  1202.      * Get referer
  1203.      *
  1204.      * @return string
  1205.      */
  1206.     public function getReferer()
  1207.     {
  1208.         return $this->referer;
  1209.     }
  1210.     /**
  1211.      * Set language
  1212.      *
  1213.      * @param string $language
  1214.      * @return Profile
  1215.      */
  1216.     public function setLanguage($language)
  1217.     {
  1218.         $this->language $language;
  1219.         return $this;
  1220.     }
  1221.     /**
  1222.      * Set last_shipping_id
  1223.      *
  1224.      * @param integer $lastShippingId
  1225.      * @return Profile
  1226.      */
  1227.     public function setLastShippingId($lastShippingId)
  1228.     {
  1229.         $this->last_shipping_id $lastShippingId;
  1230.         return $this;
  1231.     }
  1232.     /**
  1233.      * Get last_shipping_id
  1234.      *
  1235.      * @return integer
  1236.      */
  1237.     public function getLastShippingId()
  1238.     {
  1239.         return $this->last_shipping_id;
  1240.     }
  1241.     /**
  1242.      * Set last_payment_id
  1243.      *
  1244.      * @param integer $lastPaymentId
  1245.      * @return Profile
  1246.      */
  1247.     public function setLastPaymentId($lastPaymentId)
  1248.     {
  1249.         $this->last_payment_id $lastPaymentId;
  1250.         return $this;
  1251.     }
  1252.     /**
  1253.      * Get last_payment_id
  1254.      *
  1255.      * @return integer
  1256.      */
  1257.     public function getLastPaymentId()
  1258.     {
  1259.         return $this->last_payment_id;
  1260.     }
  1261.     /**
  1262.      * Set anonymous
  1263.      *
  1264.      * @param boolean $anonymous
  1265.      * @return Profile
  1266.      */
  1267.     public function setAnonymous($anonymous)
  1268.     {
  1269.         $this->anonymous $anonymous;
  1270.         return $this;
  1271.     }
  1272.     /**
  1273.      * Get anonymous
  1274.      *
  1275.      * @return boolean
  1276.      */
  1277.     public function getAnonymous()
  1278.     {
  1279.         return $this->anonymous;
  1280.     }
  1281.     /**
  1282.      * Set forceChangePassword
  1283.      *
  1284.      * @param boolean $forceChangePassword
  1285.      * @return Profile
  1286.      */
  1287.     public function setForceChangePassword($forceChangePassword)
  1288.     {
  1289.         $this->forceChangePassword $forceChangePassword;
  1290.         return $this;
  1291.     }
  1292.     /**
  1293.      * Get forceChangePassword
  1294.      *
  1295.      * @return boolean
  1296.      */
  1297.     public function getForceChangePassword()
  1298.     {
  1299.         return $this->forceChangePassword;
  1300.     }
  1301.     /**
  1302.      * Set dateOfLoginAttempt
  1303.      *
  1304.      * @param integer $dateOfLoginAttempt
  1305.      * @return Profile
  1306.      */
  1307.     public function setDateOfLoginAttempt($dateOfLoginAttempt)
  1308.     {
  1309.         $this->dateOfLoginAttempt $dateOfLoginAttempt;
  1310.         return $this;
  1311.     }
  1312.     /**
  1313.      * Get dateOfLoginAttempt
  1314.      *
  1315.      * @return integer
  1316.      */
  1317.     public function getDateOfLoginAttempt()
  1318.     {
  1319.         return $this->dateOfLoginAttempt;
  1320.     }
  1321.     /**
  1322.      * Set countOfLoginAttempts
  1323.      *
  1324.      * @param integer $countOfLoginAttempts
  1325.      * @return Profile
  1326.      */
  1327.     public function setCountOfLoginAttempts($countOfLoginAttempts)
  1328.     {
  1329.         $this->countOfLoginAttempts $countOfLoginAttempts;
  1330.         return $this;
  1331.     }
  1332.     /**
  1333.      * Get countOfLoginAttempts
  1334.      *
  1335.      * @return integer
  1336.      */
  1337.     public function getCountOfLoginAttempts()
  1338.     {
  1339.         return $this->countOfLoginAttempts;
  1340.     }
  1341.     /**
  1342.      * Set searchFakeField
  1343.      *
  1344.      * @param string $searchFakeField
  1345.      * @return Profile
  1346.      */
  1347.     public function setSearchFakeField($searchFakeField)
  1348.     {
  1349.         $this->searchFakeField $searchFakeField;
  1350.         return $this;
  1351.     }
  1352.     /**
  1353.      * Get searchFakeField
  1354.      *
  1355.      * @return string
  1356.      */
  1357.     public function getSearchFakeField()
  1358.     {
  1359.         return $this->searchFakeField;
  1360.     }
  1361.     /**
  1362.      * Set xcPendingExport
  1363.      *
  1364.      * @param boolean $xcPendingExport
  1365.      * @return Profile
  1366.      */
  1367.     public function setXcPendingExport($xcPendingExport)
  1368.     {
  1369.         $this->xcPendingExport $xcPendingExport;
  1370.         return $this;
  1371.     }
  1372.     /**
  1373.      * Get xcPendingExport
  1374.      *
  1375.      * @return boolean
  1376.      */
  1377.     public function getXcPendingExport()
  1378.     {
  1379.         return $this->xcPendingExport;
  1380.     }
  1381.     /**
  1382.      * Get order
  1383.      *
  1384.      * @return \XLite\Model\Order
  1385.      */
  1386.     public function getOrder()
  1387.     {
  1388.         return $this->order;
  1389.     }
  1390.     /**
  1391.      * Add event
  1392.      *
  1393.      * @param \XLite\Model\OrderHistoryEvents $event
  1394.      * @return Profile
  1395.      */
  1396.     public function addEvent(\XLite\Model\OrderHistoryEvents $event)
  1397.     {
  1398.         $this->event[] = $event;
  1399.         return $this;
  1400.     }
  1401.     /**
  1402.      * Get event
  1403.      *
  1404.      * @return \Doctrine\Common\Collections\Collection
  1405.      */
  1406.     public function getEvent()
  1407.     {
  1408.         return $this->event;
  1409.     }
  1410.     /**
  1411.      * Get membership
  1412.      *
  1413.      * @return \XLite\Model\Membership
  1414.      */
  1415.     public function getMembership()
  1416.     {
  1417.         return $this->membership;
  1418.     }
  1419.     /**
  1420.      * Get pending_membership
  1421.      *
  1422.      * @return \XLite\Model\Membership
  1423.      */
  1424.     public function getPendingMembership()
  1425.     {
  1426.         return $this->pending_membership;
  1427.     }
  1428.     /**
  1429.      * Add addresses
  1430.      *
  1431.      * @param \XLite\Model\Address $addresses
  1432.      * @return Profile
  1433.      */
  1434.     public function addAddresses(\XLite\Model\Address $addresses)
  1435.     {
  1436.         $this->addresses[] = $addresses;
  1437.         return $this;
  1438.     }
  1439.     /**
  1440.      * Get addresses
  1441.      *
  1442.      * @return \Doctrine\Common\Collections\Collection
  1443.      */
  1444.     public function getAddresses()
  1445.     {
  1446.         return $this->addresses;
  1447.     }
  1448.     /**
  1449.      * Add roles
  1450.      *
  1451.      * @param \XLite\Model\Role $roles
  1452.      * @return Profile
  1453.      */
  1454.     public function addRoles(\XLite\Model\Role $roles)
  1455.     {
  1456.         $this->roles[] = $roles;
  1457.         return $this;
  1458.     }
  1459.     /**
  1460.      * Get roles
  1461.      *
  1462.      * @return \Doctrine\Common\Collections\Collection|iterable<Role>
  1463.      */
  1464.     public function getRoles()
  1465.     {
  1466.         return $this->roles;
  1467.     }
  1468.     /**
  1469.      * @return string
  1470.      */
  1471.     public function getEmail()
  1472.     {
  1473.         return $this->getLastCheckoutEmail()
  1474.             ?: $this->getLogin();
  1475.     }
  1476.     /**
  1477.      * @return string
  1478.      */
  1479.     public function getLastCheckoutEmail()
  1480.     {
  1481.         return $this->lastCheckoutEmail;
  1482.     }
  1483.     /**
  1484.      * @param string $lastCheckoutEmail
  1485.      *
  1486.      * @return $this
  1487.      */
  1488.     public function setLastCheckoutEmail($lastCheckoutEmail)
  1489.     {
  1490.         $this->lastCheckoutEmail $lastCheckoutEmail;
  1491.         return $this;
  1492.     }
  1493.     /**
  1494.      * @return string
  1495.      */
  1496.     public function getSalt(): string
  1497.     {
  1498.         return implode('', [$this->login$this->password]);
  1499.     }
  1500.     public static function isAppropriatePasswordLength(string $password): bool
  1501.     {
  1502.         $passwordLength strlen($password);
  1503.         return ($passwordLength >= static::MIN_PASSWORD_LENGTH) && ($passwordLength <= static::MAX_PASSWORD_LENGTH);
  1504.     }
  1505.     public static function getPasswordLengthError(): string
  1506.     {
  1507.         return (string) Translation::lbl(
  1508.             'Password must be between X and Y characters long',
  1509.             [
  1510.                 'min_length' => static::MIN_PASSWORD_LENGTH,
  1511.                 'max_length' => static::MAX_PASSWORD_LENGTH
  1512.             ]
  1513.         );
  1514.     }
  1515. }