Skip to content
Code-Schnipsel Gruppen Projekte
MembershipUser.php 41,8 KiB
Newer Older
  • Learn to ignore specific revisions
  • Patrick Müller's avatar
    Patrick Müller committed
    <?php
    
    namespace QUI\Memberships\Users;
    
    use QUI;
    use QUI\CRUD\Child;
    
    use QUI\ERP\Accounting\Contracts\Handler as ContractsHandler;
    use QUI\ERP\Products\Handler\Products as ProductsHandler;
    use QUI\Interfaces\Users\User as QUIUserInterface;
    use QUI\Mail\Mailer;
    
    Patrick Müller's avatar
    Patrick Müller committed
    use QUI\Memberships\Handler as MembershipsHandler;
    
    Patrick Müller's avatar
    Patrick Müller committed
    use QUI\Memberships\Users\Handler as MembershipUsersHandler;
    use QUI\Memberships\Utils;
    
    use QUI\Verification\Verifier;
    
    Patrick Müller's avatar
    Patrick Müller committed
    
    /**
     * Class MembershipUser
     *
     * Represents a user that is assigned to a specific membership
     *
     * @package QUI\Memberships\Users
     */
    class MembershipUser extends Child
    {
    
        /**
         * The Membership this MembershipUser is assigned to
         *
         * @var QUI\Memberships\Membership
         */
        protected $Membership = null;
    
    
        /**
         * User that is editing this MembershipUser in the current runtime
         *
         * @var QUIUserInterface
         */
        protected $EditUser = null;
    
        /**
         * Set User that is editing this MembershipUser in the current runtime
         *
         * @param QUIUserInterface $EditUser
         */
        public function setEditUser(QUIUserInterface $EditUser)
        {
            $this->EditUser = $EditUser;
        }
    
    
    Patrick Müller's avatar
    Patrick Müller committed
        /**
         * @inheritdoc
    
         * @param bool $withPermission - check permissions on update [default: true]
    
    Patrick Müller's avatar
    Patrick Müller committed
         */
    
    Patrick Müller's avatar
    Patrick Müller committed
        public function update(): void
    
            Permission::checkPermission(MembershipUsersHandler::PERMISSION_EDIT_USERS, $this->EditUser);
    
    Patrick Müller's avatar
    Patrick Müller committed
            // check certain attributes
    
            if (!$this->getMembership()->isInfinite()) {
                $beginDate = strtotime($this->getAttribute('beginDate'));
    
                $endDate = strtotime($this->getAttribute('endDate'));
    
                if (
                    $beginDate === false
    
                    || $endDate === false
                ) {
    
                    throw new QUI\Memberships\Exception([
    
                        'quiqqer/memberships',
    
                        'exception.users.membershipuser.wrong.dates',
                        [
                            'id' => $this->getId()
                        ]
    
                if ($beginDate >= $endDate) {
    
                    throw new QUI\Memberships\Exception([
    
                        'quiqqer/memberships',
    
                        'exception.users.membershipuser.begin.after.end',
                        [
                            'id' => $this->getId()
                        ]
    
            }
    
            // check dates
            foreach ($this->getAttributes() as $k => $v) {
                switch ($k) {
                    case 'beginDate':
                    case 'endDate':
    
                    case 'addedDate':
    
                    case 'cancelDate':
                    case 'archiveDate':
                        if (empty($v) || $v === '0000-00-00 00:00:00') {
                            $this->setAttribute($k, null);
    
                        } else {
                            $this->setAttribute($k, Utils::getFormattedTimestamp($v));
    
                    case 'cancelled':
                        $this->setAttribute($k, $v ? 1 : 0);
                        break;
    
    Patrick Müller's avatar
    Patrick Müller committed
            }
    
            parent::update();
        }
    
    
    Patrick Müller's avatar
    Patrick Müller committed
        /**
    
         * Extend the current membership cycle of this membership user
    
         * @param bool $auto (optional) - Used if the membership is automatically extended.
         * If set to false, the setting membershipusers.extendMode is used [default: true]
    
         * @param \DateTime $NextBeginDate (optional) - New cycle begin date
         * @param \DateTime $NextEndDate (optional) - New cycle end date
    
    Patrick Müller's avatar
    Patrick Müller committed
         * @return void
    
        public function extend($auto = true, \DateTime $NextBeginDate = null, \DateTime $NextEndDate = null)
    
            // Calculate new start and/or end time
    
                if (MembershipUsersHandler::getExtendMode() === MembershipUsersHandler::EXTEND_MODE_PROLONG) {
                    $NextBeginDate = $this->getCycleBeginDate();
                } else {
                    $NextBeginDate = $this->getNextCycleBeginDate();
                }
    
            }
    
            if (empty($NextEndDate)) {
                $NextEndDate = $this->getNextCycleEndDate();
            }
    
                $extendCounter = $this->getAttribute('extendCounter');
    
                    'beginDate' => Utils::getFormattedTimestamp($NextBeginDate),
                    'endDate' => Utils::getFormattedTimestamp($NextEndDate),
    
                    'extendCounter' => $extendCounter + 1
    
                    'endDate' => Utils::getFormattedTimestamp($NextEndDate)
    
                'start' => Utils::getFormattedTimestamp($NextBeginDate),
    
                'end' => Utils::getFormattedTimestamp($NextEndDate),
                'auto' => $auto ? '1' : '0'
    
            $this->addHistoryEntry(MembershipUsersHandler::HISTORY_TYPE_EXTENDED, json_encode($historyData));
    
            // send mail
            if ($auto) {
                $this->sendAutoExtendMail();
            } else {
                $this->sendManualExtendMail();
            }
        }
    
    
        /**
         * Calculate the end date of the current cycle based on a start date
         *
         * @param \DateTime $Start (optional) - Calculate based on this start date [default: now]
         * @return \DateTime
         */
        public function calcEndDate($Start = null)
        {
            if (empty($Start)) {
                $Start = \date_create();
            }
    
            $contractId = $this->getContractId();
            $NewEndDate = \date_create($this->getMembership()->calcEndDate($Start->getTimestamp()));
    
            if (empty($contractId)) {
                return $NewEndDate;
            }
    
            try {
    
                $Contract = ContractsHandler::getInstance()->getContract($contractId);
    
                $ContractExtensionInterval = $Contract->getExtensionInterval();
            } catch (\Exception $Exception) {
                QUI\System\Log::writeException($Exception);
                return $NewEndDate;
            }
    
            if (!$ContractExtensionInterval) {
                return $NewEndDate;
            }
    
    
            $NewEndDate = $Start->add($ContractExtensionInterval);
    
            switch (MembershipUsersHandler::getDurationMode()) {
                case MembershipUsersHandler::DURATION_MODE_DAY:
    
                    $NewEndDate->add(new \DateInterval('P1D'));
                    $NewEndDate->setTime(23, 59, 59);
                    break;
            }
    
            return $NewEndDate;
        }
    
    
        /**
         * Send mail to the user if the membership is extended automatically
         *
         * @return void
         */
        protected function sendAutoExtendMail()
        {
            $sendMail = MembershipUsersHandler::getSetting('sendAutoExtendMail');
    
            if (!$sendMail) {
                return;
            }
    
    
            try {
                $subject = $this->getUser()->getLocale()->get(
                    'quiqqer/memberships',
                    'templates.mail.autoextend.subject'
                );
    
                $this->sendMail($subject, dirname(__FILE__, 5) . '/templates/mail_autoextend.html');
    
            } catch (\Exception $Exception) {
                QUI\System\Log::writeException($Exception);
            }
    
        /**
         * Send mail to the user if the membership is extended manually
         *
         * Manually = Either by admin edit or if the user is re-added to the membership
         * although he already is a member
         *
         * @return void
         */
        public function sendManualExtendMail()
        {
            $sendMail = MembershipUsersHandler::getSetting('sendManualExtendMail');
    
            if (!$sendMail) {
                return;
            }
    
    
            try {
                $subject = $this->getUser()->getLocale()->get(
                    'quiqqer/memberships',
                    'templates.mail.manualextend.subject'
                );
    
                $this->sendMail($subject, dirname(__FILE__, 5) . '/templates/mail_manualextend.html');
    
            } catch (\Exception $Exception) {
                QUI\System\Log::writeException($Exception);
            }
    
        /**
         * Expires this memberships user
         *
         * @return void
    
         */
        public function expire()
        {
            $this->addHistoryEntry(MembershipUsersHandler::HISTORY_TYPE_EXPIRED);
            $this->archive(MembershipUsersHandler::ARCHIVE_REASON_EXPIRED);
    
            // send expire mail
            $subject = $this->getUser()->getLocale()->get('quiqqer/memberships', 'templates.mail.expired.subject');
    
            $this->sendMail($subject, dirname(__FILE__, 5) . '/templates/mail_expired.html');
    
    
            QUI::getEvents()->fireEvent('quiqqerMembershipsExpired', [$this]);
    
         * Start the manual membership cancellation process
         *
         * Generates a random hash and sends an email to the user
    
    Patrick Müller's avatar
    Patrick Müller committed
         *
         * @return void
    
         *
         * @throws QUI\Memberships\Exception
    
         * @throws QUI\Verification\Exception
         * @throws QUI\Exception
    
        public function startManualCancel()
    
            // check cancel permission
            if ((int)QUI::getUserBySession()->getId() !== (int)$this->getUserId()) {
    
                throw new QUI\Memberships\Exception([
    
                    'quiqqer/memberships',
                    'exception.users.membershipuser.manualcancel.no.permission'
    
    Patrick Müller's avatar
    Patrick Müller committed
            if ($this->isCancelled()) {
    
                return;
            }
    
            $Membership = $this->getMembership();
    
            // cannot manually cancel infinite memberships
            if ($Membership->isInfinite()) {
                return;
            }
    
            // cannot manually cancel default membership
            if ($Membership->isDefault()) {
    
            $userEmail = $this->getUser()->getAttribute('email');
    
            if (empty($userEmail)) {
    
                throw new QUI\Memberships\Exception([
    
                    'quiqqer/memberships',
                    'exception.users.membershipuser.manualcancel.no_email_address'
    
            $cancelUrl = Verifier::startVerification($this->getCancelVerification(), true);
            $cancelDate = Utils::getFormattedTimestamp();
    
            $CancelEndDate = $this->getCurrentCancelEndDate();
    
                'cancelStatus' => MembershipUsersHandler::CANCEL_STATUS_CANCEL_CONFIRM_PENDING,
                'cancelDate' => $cancelDate,
    
                'cancelEndDate' => $CancelEndDate->format('Y-m-d H:i:s')
    
    Patrick Müller's avatar
    Patrick Müller committed
            $this->addHistoryEntry(MembershipUsersHandler::HISTORY_TYPE_CANCEL_START);
    
    
    Patrick Müller's avatar
    Patrick Müller committed
            // save cancel hash and date to database
    
            $this->setEditUser(QUI::getUsers()->getSystemUser());
            $this->update();
    
    Patrick Müller's avatar
    Patrick Müller committed
    
            // send cancellation mail
    
            $this->sendMail(
                QUI::getLocale()->get('quiqqer/memberships', 'templates.mail.startcancel.subject'),
    
                dirname(__FILE__, 5) . '/templates/mail_startcancel.html',
    
                    'cancelDate' => $cancelDate,
                    'cancelUrl' => $cancelUrl,
    
                    'cancelEndDate' => $User->getLocale()->formatDate($CancelEndDate->getTimestamp())
    
        /**
         * Automatic cancellation of a MembershipUser.
         *
         * HINT: This is not supposed to be executed by the user, but programmatically only if
         * a membership needs to be cancelled for other reasons than a manual cancellation by the user.
         *
         * A user CANNOT revoke a cancellation executed this way!
         *
         * @return void
         * @throws \Exception
         */
        public function autoCancel()
        {
            if ($this->isCancelled()) {
                return;
            }
    
            $Membership = $this->getMembership();
    
            // cannot cancel infinite memberships
            if ($Membership->isInfinite()) {
                return;
            }
    
            // cannot cancel default membership
            if ($Membership->isDefault()) {
                return;
            }
    
            $cancelDate = Utils::getFormattedTimestamp();
    
            $this->setAttributes([
    
                'cancelStatus' => MembershipUsersHandler::CANCEL_STATUS_CANCELLED_BY_SYSTEM,
    
                'cancelDate' => $cancelDate,
                'cancelled' => true
    
            ]);
    
            $this->addHistoryEntry(MembershipUsersHandler::HISTORY_TYPE_CANCEL_START_SYSTEM);
    
            // save cancel hash and date to database
            $this->setEditUser(QUI::getUsers()->getSystemUser());
    
            try {
                $this->update();
                QUI::getEvents()->fireEvent('quiqqerMembershipsAutoCancel', [$this]);
            } catch (\Exception $Exception) {
                QUI\System\Log::writeException($Exception);
            }
        }
    
    
         * Start to abort a manually started cancellation process
    
         *
         * @return void
         * @throws QUI\Memberships\Exception
    
         * @throws QUI\Verification\Exception
         * @throws QUI\Exception
    
        public function startAbortCancel()
    
        {
            // check cancel permission
            if ((int)QUI::getUserBySession()->getId() !== (int)$this->getUserId()) {
    
                throw new QUI\Memberships\Exception([
    
                    'quiqqer/memberships',
                    'exception.users.membershipuser.manualcancel.no.permission'
    
            $cancelStatus = (int)$this->getAttribute('cancelStatus');
    
    
            // If cancellation was initiated programmatically (by system), a user cannot undo this
            if ($cancelStatus === MembershipUsersHandler::CANCEL_STATUS_CANCELLED_BY_SYSTEM) {
                throw new QUI\Memberships\Exception([
                    'quiqqer/memberships',
                    'exception.users.membershipuser.manualcancel.no_system_uncancel'
                ]);
            }
    
    
            if (
                $cancelStatus !== MembershipUsersHandler::CANCEL_STATUS_CANCEL_CONFIRM_PENDING
    
                && $cancelStatus !== MembershipUsersHandler::CANCEL_STATUS_CANCELLED
    
            $userEmail = $this->getUser()->getAttribute('email');
    
            if (empty($userEmail)) {
    
                throw new QUI\Memberships\Exception([
    
                    'quiqqer/memberships',
                    'exception.users.membershipuser.abortcancel.no_email_address'
    
            $abortCancelUrl = Verifier::startVerification($this->getAbortCancelVerification(), true);
    
    
                'cancelStatus' => MembershipUsersHandler::CANCEL_STATUS_ABORT_CANCEL_CONFIRM_PENDING,
    
            $this->addHistoryEntry(MembershipUsersHandler::HISTORY_TYPE_CANCEL_ABORT_START);
    
            $this->setEditUser(QUI::getUsers()->getSystemUser());
            $this->update();
    
    
            // send abort cancellation mail
            $this->sendMail(
                QUI::getLocale()->get('quiqqer/memberships', 'templates.mail.startabortcancel.subject'),
    
                dirname(__FILE__, 5) . '/templates/mail_startabortcancel.html',
    
                    'abortCancelUrl' => $abortCancelUrl
    
            );
        }
    
        /**
         * Confirm abortion of cancellation
         *
         * @return void
         */
        public function confirmAbortCancel()
        {
    
                'cancelDate' => null,
                'cancelStatus' => MembershipUsersHandler::CANCEL_STATUS_NOT_CANCELLED,
                'cancelled' => false,
    
            try {
                Verifier::removeVerification($this->getAbortCancelVerification());
            } catch (\Exception $Exception) {
                QUI\System\Log::writeException($Exception);
            }
    
    
            $this->addHistoryEntry(MembershipUsersHandler::HISTORY_TYPE_CANCEL_ABORT_CONFIRM);
    
            $this->setEditUser(QUI::getUsers()->getSystemUser());
    
            try {
                $this->update();
                QUI::getEvents()->fireEvent('quiqqerMembershipsCancelAbort', [$this]);
            } catch (\Exception $Exception) {
                QUI\System\Log::writeException($Exception);
            }
    
    Patrick Müller's avatar
    Patrick Müller committed
        /**
    
    Patrick Müller's avatar
    Patrick Müller committed
         *
         * @return void
         * @throws QUI\Memberships\Exception
    
        public function confirmManualCancel()
    
    Patrick Müller's avatar
    Patrick Müller committed
            if ($this->isCancelled()) {
    
                'cancelled' => true,
                'cancelStatus' => MembershipUsersHandler::CANCEL_STATUS_CANCELLED,
    
                'cancelEndDate' => $this->getCurrentCancelEndDate()->format('Y-m-d H:i:s')
    
    
            $this->addHistoryEntry(MembershipUsersHandler::HISTORY_TYPE_CANCEL_CONFIRM);
    
    Patrick Müller's avatar
    Patrick Müller committed
            $this->update();
    
    
            // send confirm cancel mail
    
            $this->sendConfirmCancelMail();
    
    
            QUI::getEvents()->fireEvent('quiqqerMembershipsCancelConfirm', [$this]);
    
        }
    
        /**
         * Send mail to user to confirm cancellation
         *
         * @return void
         */
        public function sendConfirmCancelMail()
        {
    
            try {
                $subject = $this->getUser()->getLocale()->get(
                    'quiqqer/memberships',
                    'templates.mail.confirmcancel.subject'
                );
    
    
                $this->sendMail($subject, dirname(__FILE__, 5) . '/templates/mail_confirmcancel.html');
    
            } catch (\Exception $Exception) {
                QUI\System\Log::writeException($Exception);
            }
    
        /**
         * Send e-mail to remind user of an outstanding cancellation confirmation.
         *
    
         */
        public function sendConfirmCancelReminderMail()
        {
            try {
                $subject = $this->getUser()->getLocale()->get(
                    'quiqqer/memberships',
                    'templates.mail.confirmcancel_reminder.subject'
                );
    
                $this->sendMail(
                    $subject,
    
                    dirname(__FILE__, 5) . '/templates/mail_confirmcancel_reminder.html',
    
                    [
                        'cancelUrl' => Verifier::getVerificationUrl($this->getCancelVerification())
                    ]
                );
    
                $this->addHistoryEntry(
                    \QUI\Memberships\Users\Handler::HISTORY_TYPE_MISC,
                    QUI::getLocale()->get(
                        'quiqqer/memberships',
                        'history.MembershipUser.cancel_confirm_reminder_sent'
                    )
                );
    
                $this->EditUser = QUI::getUsers()->getSystemUser();
                $this->update();
    
    
            } catch (\Exception $Exception) {
                QUI\System\Log::writeException($Exception);
    
    Patrick Müller's avatar
    Patrick Müller committed
        /**
    
         * Cancel membership
    
    Patrick Müller's avatar
    Patrick Müller committed
         *
         * @return void
    
        public function cancel()
    
            $this->archive(MembershipUsersHandler::ARCHIVE_REASON_CANCELLED);
    
            // send expired mail
            $subject = $this->getUser()->getLocale()->get('quiqqer/memberships', 'templates.mail.expired.subject');
    
            $this->sendMail($subject, dirname(__FILE__, 5) . '/templates/mail_cancelled.html');
    
    
            QUI::getEvents()->fireEvent('quiqqerMembershipsCancelled', [$this]);
    
    Patrick Müller's avatar
    Patrick Müller committed
        }
    
        /**
         * Check if this user has cancelled his membership
         *
         * @return mixed
         */
        public function isCancelled()
        {
    
            return boolval($this->getAttribute('cancelled'));
    
        /**
         * Delete membership user and remove QUIQQER user from all unique groups
         *
    
    Patrick Müller's avatar
    Patrick Müller committed
         * A deleted membership user is not removed from the database but set to "archived".
         *
    
    Patrick Müller's avatar
    Patrick Müller committed
        public function delete(): void
    
            Permission::checkPermission(MembershipUsersHandler::PERMISSION_EDIT_USERS, $this->EditUser);
    
    Patrick Müller's avatar
    Patrick Müller committed
            $this->addHistoryEntry(MembershipUsersHandler::HISTORY_TYPE_DELETED);
    
            // do not delete, just set to archived
    
            $this->archive(MembershipUsersHandler::ARCHIVE_REASON_DELETED);
    
    
            QUI::getEvents()->fireEvent('quiqqerMembershipsUserDelete', [$this]);
    
    Patrick Müller's avatar
    Patrick Müller committed
        }
    
        /**
         * Set User to all membership QUIQQER groups
         *
         * @return void
         */
    
        public function addToGroups()
    
    Patrick Müller's avatar
    Patrick Müller committed
        {
            $groupIds = $this->getMembership()->getGroupIds();
    
            $User = $this->getUser();
    
    Patrick Müller's avatar
    Patrick Müller committed
    
            foreach ($groupIds as $groupId) {
                $User->addToGroup($groupId);
            }
    
            $User->save(QUI::getUsers()->getSystemUser());
        }
    
        /**
         * Removes the membership user from all quiqqer groups (that he is part of because of
         * his membership)
         *
         * @return void
    
    Patrick Müller's avatar
    Patrick Müller committed
         */
        protected function removeFromGroups()
    
            /**
             * Check if the user exists first. If he does NOT, then he does not need to be removed
             * from QUIQQER groups (anymore).
             */
            try {
                $User = QUI::getUsers()->get($this->getUserId());
            } catch (\Exception $Exception) {
                if ($Exception->getCode() === 404) {
                    return;
                }
    
                QUI\System\Log::writeException($Exception);
                return;
            }
    
    
            $Groups = QUI::getGroups();
            $Memberships = MembershipsHandler::getInstance();
            $Membership = $this->getMembership();
    
            $membershipGroupIds = $Membership->getGroupIds();
    
            // remove user from unique group ids
            foreach ($Membership->getUniqueGroupIds() as $groupId) {
                $Groups->get($groupId)->removeUser($User);
    
                $k = array_search($groupId, $membershipGroupIds);
    
                if ($k !== false) {
                    unset($membershipGroupIds[$k]);
                }
    
    
            // remove user from all non-unique group ids where the user is not part of
            // the membership
            foreach ($membershipGroupIds as $groupId) {
    
                foreach ($Memberships->getMembershipIdsByGroupIds([$groupId]) as $membershipId) {
    
                    $OtherMembership = $Memberships->getChild($membershipId);
    
                    if (!$OtherMembership->hasMembershipUserId($User->getId())) {
                        $User->removeGroup($groupId);
                    }
                }
            }
    
            $User->save(QUI::getUsers()->getSystemUser());
    
    Patrick Müller's avatar
    Patrick Müller committed
        /**
         * Archive this membership user
         *
    
         * @param string $reason - The reason why this user is archived
    
    Patrick Müller's avatar
    Patrick Müller committed
         * @return void
         */
    
        public function archive($reason)
    
            $this->removeFromGroups();
    
            $this->addHistoryEntry(MembershipUsersHandler::HISTORY_TYPE_ARCHIVED, [
    
                'archived' => 1,
                'archiveDate' => Utils::getFormattedTimestamp(),
    
                'archiveReason' => $reason
    
    Patrick Müller's avatar
    Patrick Müller committed
            $this->update();
        }
    
        /**
         * Checks if this membership user is archived
         *
         * @retun bool
         */
        public function isArchived()
        {
            return boolval($this->getAttribute('archived'));
    
    Patrick Müller's avatar
    Patrick Müller committed
        /**
         * Get the Membership this membership user is assigned to
         *
         * @return QUI\Memberships\Membership
         */
        public function getMembership()
        {
    
            if ($this->Membership) {
                return $this->Membership;
            }
    
            $this->Membership = MembershipsHandler::getInstance()->getChild(
    
    Patrick Müller's avatar
    Patrick Müller committed
                $this->getAttribute('membershipId')
            );
    
    
            return $this->Membership;
    
    Patrick Müller's avatar
    Patrick Müller committed
        }
    
        /**
         * Get QUIQQER User ID of membership user
         *
         * @return int
         */
        public function getUserId()
        {
            return (int)$this->getAttribute('userId');
        }
    
    Patrick Müller's avatar
    Patrick Müller committed
    
        /**
         * Get QUIQQER User
         *
         * @return QUI\Users\User
    
        public function getUser(): ?QUI\Users\User
    
            try {
                return QUI::getUsers()->get($this->getUserId());
            } catch (\Exception $Exception) {
                QUI\System\Log::writeException($Exception);
                return null;
            }
    
        /**
         * Get ID of the Contract if this MembershipUser was created due to a
         * contract.
         *
         * @return int|false
         */
        public function getContractId()
        {
            $contractId = $this->getAttribute('contractId');
    
            if (empty($contractId)) {
                return false;
            }
    
            return (int)$contractId;
        }
    
    
        /**
         * Get contract that is currently associated to this MembershipUser
         *
         * @return false|QUI\ERP\Accounting\Contracts\Contract
         */
        public function getContract()
        {
            $contractId = $this->getContractId();
    
            if (!$contractId) {
                return false;
            }
    
            try {
                return QUI\ERP\Accounting\Contracts\Handler::getInstance()->get($contractId);
            } catch (\Exception $Exception) {
                QUI\System\Log::writeException($Exception);
                return false;
            }
        }
    
    
        /**
         * Permanently links this MembershipUser to a contract (quiqqer/contracts)
         *
         * This causes the end date of this MembershipUser to be equal with the contract end date.
         *
         * @param int $contractId
         * @return void
         * @throws QUI\Exception
         */
        public function linkToContract($contractId)
        {
            try {
    
                $Contract = ContractsHandler::getInstance()->getContract($contractId);
    
                $ContractCycleEndDate = $Contract->getCycleEndDate();
            } catch (\Exception $Exception) {
                QUI\System\Log::writeException($Exception);
                return;
            }
    
            $this->setAttribute('contractId', $contractId);
    
            if ($ContractCycleEndDate) {
                $this->setAttribute('endDate', $ContractCycleEndDate->format('Y-m-d 23:59:59'));
            }
    
            $this->update();
        }
    
    
    Patrick Müller's avatar
    Patrick Müller committed
        /**
         * Add an entry to the membership user history
         *
         * @param string $type - History entry type (see \QUI\Memberships\Users\Handler)
         * @param string $msg (optional) - additional custom message
         */
    
    Patrick Müller's avatar
    Patrick Müller committed
        public function addHistoryEntry($type, $msg = "")
    
            $history = $this->getHistory();
    
            if (empty($msg)) {
                $msg = "";
            }
    
    
            if (is_array($msg)) {
                $msg = json_encode($msg);
            }
    
    
    Patrick Müller's avatar
    Patrick Müller committed
            $User = QUI::getUserBySession();
    
    
    Patrick Müller's avatar
    Patrick Müller committed
                'type' => $type,
                'time' => Utils::getFormattedTimestamp(),
    
                'user' => $User->getName() . ' (' . $User->getId() . ')',
                'msg' => $msg
    
    Patrick Müller's avatar
    Patrick Müller committed
    
            $this->setAttribute('history', json_encode($history));
        }
    
    Patrick Müller's avatar
    Patrick Müller committed
        /**
         * Get history data of this MembershipUser
         *
         * @return array
         */
        public function getHistory()
        {
            $history = $this->getAttribute('history');
    
            if (empty($history)) {
    
    Patrick Müller's avatar
    Patrick Müller committed
            } else {
                $history = json_decode($history, true);
            }
    
            return $history;
        }
    
    
        /**
         * Format date based on User Locale and duration mode
         *
    
         * @param string|\DateTime $date - Formatted date YYYY-MM-DD HH:MM:SS or \DateTime object
    
         * @return string|false - formatted date or false on error
    
            if (empty($date) || $date === '0000-00-00 00:00:00') {
    
                return false;
    
            } elseif ($date instanceof \DateTime) {
                $date = $date->format('Y-m-d H:i:s');
    
            $Locale = $this->getUser()->getLocale();
    
            $lang = $Locale->getCurrent();
            $Conf = QUI::getPackage('quiqqer/memberships')->getConfig();
    
            switch (MembershipUsersHandler::getDurationMode()) {
                case MembershipUsersHandler::DURATION_MODE_DAY:
    
                    $dateFormat = $Conf->get('date_formats_short', $lang);
    
                    // fallback to default value
                    if (empty($dateFormat)) {
                        $dateFormat = '%D';
                    }
    
                    $dateFormat = $Conf->get('date_formats_long', $lang);
    
                    // fallback to default value
                    if (empty($dateFormat)) {
                        $dateFormat = '%D %H:%M';
                    }
    
            return $Locale->formatDate(strtotime($date), $dateFormat);
    
        }
    
        /**
         * Get membership data for frontend view/edit purposes with correctly formatted dates
         *
         * @return array
         */
        public function getFrontendViewData()
        {
            $QuiqqerUser = $this->getUser();
    
            $Membership = $this->getMembership();
            $Locale = QUI::getLocale();
    
            // determine source of title, short and content
    
            $viewDataMode = MembershipUsersHandler::getSetting('viewDataMode');
    
            $productId = $this->getAttribute('productId');
    
            if (
                $viewDataMode === 'product'
    
                && !empty($productId)
                && Utils::isQuiqqerProductsInstalled()
            ) {
    
                $Product = ProductsHandler::getProduct($productId);
                $title = $Product->getTitle($Locale);
    
                $description = $Product->getDescription($Locale);
    
                $content = '';
    
                $title = $Membership->getTitle($Locale);
    
                $description = $Membership->getDescription($Locale);
    
                $content = $Membership->getContent($Locale);
    
            $CurrentCancelEndDate = $this->getCurrentCancelEndDate();
    
            $CancelUntilDate = false;
            $cancelAllowed = !$this->isCancelled();
            $Contract = $this->getContract();
    
    
            if (!$this->isCancelled() && $Contract) {
                try {
                    if (!$Contract->isInPeriodOfNotice()) {
                        $cancelAllowed = false;
                    }
    
    
                    $PeriodOfNoticeInterval = $Contract->getPeriodOfNoticeInterval();
    
                    $EndBaseDate = clone $CurrentCancelEndDate;
    
                    $EndBaseDate->setTime(0, 0, 0);
                    $EndBaseDate->sub(\date_interval_create_from_date_string('1 second'));
    
    
                    $CancelUntilDate = clone $EndBaseDate;
    
                    if ($PeriodOfNoticeInterval) {
                        $CancelUntilDate = $EndBaseDate->sub($PeriodOfNoticeInterval);
                    }
    
                } catch (\Exception $Exception) {
                    QUI\System\Log::writeException($Exception);
                }
            }
    
    
            $addedDate = $this->formatDate($this->getAttribute('addedDate'));
            $CycleEndDate = $this->getCycleEndDate();
            $cycleEndDate = $CycleEndDate ? $this->formatDate($CycleEndDate) : '-';
            $cycleBeginDate = $this->formatDate($this->getCycleBeginDate());
    
            $NextCycleEndDate = $this->getNextCycleEndDate();
            $nextCycleEndDate = $NextCycleEndDate ? $this->formatDate($NextCycleEndDate) : '-';
    
            // Determine cancel info text
            if ($Contract) {
    
                if ($Contract->getPeriodOfNoticeInterval()) {