Skip to content
Code-Schnipsel Gruppen Projekte
MembershipUser.php 41 KiB
Newer Older
Patrick Müller's avatar
Patrick Müller committed
<?php

namespace QUI\Memberships\Users;

use QUI;
use QUI\CRUD\Child;
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\Mail\Mailer;
use QUI\Verification\Verifier;
use QUI\ERP\Products\Handler\Products as ProductsHandler;
use QUI\ERP\Accounting\Contracts\Handler as ContractsHandler;
use QUI\Interfaces\Users\User as QUIUserInterface;
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
    {
        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'
            if ($beginDate >= $endDate) {
                throw new QUI\Memberships\Exception([
                    'quiqqer/memberships',
                    'exception.users.membershipuser.begin.after.end'
        }

        // 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]
Patrick Müller's avatar
Patrick Müller committed
     * @return void
Patrick Müller's avatar
Patrick Müller committed
     */
    public function extend($auto = true)
        // Calculate new start and/or end time
        $NextBeginDate = $this->getNextCycleBeginDate();
        $NextEndDate   = $this->getNextCycleEndDate();

        if ($auto) {
            $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),
        $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
Patrick Müller's avatar
Patrick Müller committed
     */
    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,
            'cancelEndDate' => null
        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
Patrick Müller's avatar
Patrick Müller committed
     */
    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())
                ]
            );
        } 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
Patrick Müller's avatar
Patrick Müller committed
     */
    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".
     *
     * @return void
     */
    public function delete()
        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();

        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
Patrick Müller's avatar
Patrick Müller committed
     */
    public function getUser()
    {
        return QUI::getUsers()->get($this->getUserId());
    }

    /**
     * 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().')',
Patrick Müller's avatar
Patrick Müller committed
            '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();
        // 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     = '';
        } else {
            $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 = $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()) {
                if ($Contract->isInPeriodOfNotice()) {
                    $cancelInfoText = QUI::getLocale()->get(
                        'quiqqer/memberships',
                        'MembershipUser.cancel.info_text.cancel_until_date',
                        [
                            'addedDate'        => $addedDate,
                            'cancelUntilDate'  => $this->formatDate($CancelUntilDate),
                            'cycleEndDate'     => $cycleEndDate,
                            'nextCycleEndDate' => $nextCycleEndDate
                        ]
                    );
                } else {
                    $cancelInfoText = QUI::getLocale()->get(
                        'quiqqer/memberships',
                        'MembershipUser.cancel.info_text.period_of_notice_expired',
                        [
                            'addedDate'        => $addedDate,
                            'cancelUntilDate'  => $this->formatDate($CancelUntilDate),
                            'cycleBeginDate'   => $cycleBeginDate,
                            'cycleEndDate'     => $cycleEndDate,
                            'nextCycleEndDate' => $nextCycleEndDate
                        ]
                    );
                }
            } else {
                $cancelInfoText = QUI::getLocale()->get(
                    'quiqqer/memberships',
                    'MembershipUser.cancel.info_text.cycle_cancel_anytime',
                    [
                        'addedDate'        => $addedDate,
                        'cycleEndDate'     => $cycleEndDate,
                        'nextCycleEndDate' => $nextCycleEndDate
                    ]
                );
            }
        } elseif ($this->getMembership()->isInfinite()) {
            $cancelInfoText = QUI::getLocale()->get(
                'quiqqer/memberships',
                'MembershipUser.cancel.info_text.cancel_anytime'