billwerk_subscriptions-1.x-dev/modules/billwerk_subscriptions_manage/src/Plugin/Block/BillwerkUserSubscriptionsBlock.php

modules/billwerk_subscriptions_manage/src/Plugin/Block/BillwerkUserSubscriptionsBlock.php
<?php

declare(strict_types=1);

namespace Drupal\billwerk_subscriptions_manage\Plugin\Block;

use Drupal\Component\Serialization\Json;
use Drupal\Core\Access\AccessResult;
use Drupal\Core\Block\BlockBase;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Language\LanguageManagerInterface;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\Core\Render\Element;
use Drupal\Core\Render\RendererInterface;
use Drupal\Core\Routing\CurrentRouteMatch;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\Session\AccountProxyInterface;
use Drupal\Core\Url;
use Drupal\billwerk_subscriptions\Environment;
use Drupal\billwerk_subscriptions\Subscriber;
use Drupal\billwerk_subscriptions_entities\BillwerkEntitiesHelper;
use Drupal\billwerk_subscriptions_entities\SubscriberBillwerkEntitiesHelper;
use Drupal\billwerk_subscriptions_manage\Form\SettingsForm;
use Drupal\billwerk_subscriptions_manage\Helper;
use Drupal\user\UserInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;

/**
 * Provides a billwerk user subscriptions block.
 *
 * @Block(
 *   id = "billwerk_subscriptions_manage_billwerk_user_subscriptions",
 *   admin_label = @Translation("Billwerk Subscriptions Manage: My Subscription"),
 *   category = @Translation("Billwerk Subscriptions"),
 * )
 */
final class BillwerkUserSubscriptionsBlock extends BlockBase implements ContainerFactoryPluginInterface {

  /**
   * The subscriber billwerk entities helper.
   *
   * @var \Drupal\billwerk_subscriptions_entities\SubscriberBillwerkEntitiesHelper
   */
  protected readonly SubscriberBillwerkEntitiesHelper $subscriberBillwerkEntitiesHelper;

  /**
   * The owner of the block.
   *
   * @var ?\Drupal\Core\Session\AccountInterface
   */
  protected readonly ?UserInterface $owner;

  /**
   * The subscriber.
   *
   * @var ?\Drupal\billwerk_subscriptions\Subscriber
   */
  protected readonly ?Subscriber $subscriber;

  /**
   * Constructs the plugin instance.
   */
  public function __construct(
    array $configuration,
    $plugin_id,
    $plugin_definition,
    protected readonly CurrentRouteMatch $currentRouteMatch,
    protected readonly AccountProxyInterface $currentUser,
    protected readonly EntityTypeManagerInterface $entityTypeManager,
    protected readonly BillwerkEntitiesHelper $billwerkEntitiesHelper,
    protected readonly Environment $environment,
    protected readonly LanguageManagerInterface $languageManager,
    protected readonly RendererInterface $renderer,
    protected readonly Helper $helper,
  ) {
    parent::__construct($configuration, $plugin_id, $plugin_definition);
    switch ($this->configuration['source']) {
      case 'current':
        $this->owner = $entityTypeManager->getStorage('user')->load($currentUser->id());
        break;

      case 'param_user':
        $this->owner = $currentRouteMatch->getParameter('user');
        break;

      default:
        throw new \UnexpectedValueException("Unexpected source: \"{$this->configuration['source']}\"");
    }
    if (!$this->owner || $this->owner->isAnonymous()) {
      // The anonymous user may never be a subscriber!
      return;
    }
    $this->subscriber = Subscriber::load($this->owner);
    $this->subscriberBillwerkEntitiesHelper = SubscriberBillwerkEntitiesHelper::create($this->subscriber, $billwerkEntitiesHelper, $environment);
  }

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition): self {
    return new self(
      $configuration,
      $plugin_id,
      $plugin_definition,
      $container->get('current_route_match'),
      $container->get('current_user'),
      $container->get('entity_type.manager'),
      $container->get('billwerk_subscriptions_entities.billwerk_entities_helper'),
      $container->get('billwerk_subscriptions.environment'),
      $container->get('language_manager'),
      $container->get('renderer'),
      $container->get('billwerk_subscriptions_manage.helper'),
    );
  }

  /**
   * {@inheritdoc}
   */
  public function defaultConfiguration(): array {
    return [
      'source' => 'current',
      'show_active_subscription' => TRUE,
      'show_active_components' => TRUE,
      'no_active_components_text' => [
        'value' => '',
        'format' => 'full_html',
      ],
      'show_change_plan_variant_button' => TRUE,
      'show_cancel_plan_variant_button' => FALSE,
      'show_components_subscribe_button' => TRUE,
      'show_components_unsubscribe_button' => TRUE,
      'modal_actions' => TRUE,
    ];
  }

  /**
   * {@inheritdoc}
   */
  public function blockForm($form, FormStateInterface $form_state): array {
    $form['source'] = [
      '#type' => 'select',
      '#title' => $this->t('User source'),
      '#options' => [
        'current' => $this->t('Current user'),
        'param_user' => $this->t('Route user account (Route parameter "user)'),
      ],
      '#default_value' => $this->configuration['source'],
    ];
    $form['show_active_subscription'] = [
      '#type' => 'checkbox',
      '#title' => $this->t('Show active subscriptions'),
      '#default_value' => $this->configuration['show_active_subscription'],
    ];
    $form['show_active_components'] = [
      '#type' => 'checkbox',
      '#title' => $this->t('Show active components'),
      '#default_value' => $this->configuration['show_active_components'],
    ];
    $form['no_active_components_text'] = [
      '#type' => 'text_format',
      '#title' => $this->t('No active components text'),
      '#default_value' => $this->configuration['no_active_components_text']['value'],
      '#format' => $this->configuration['no_active_components_text']['format'],
      '#description' => $this->t('If left empty, the components list will be hidden.'),
    ];
    $form['show_change_plan_variant_button'] = [
      '#type' => 'checkbox',
      '#title' => $this->t('Show the "Change plan" button'),
      '#default_value' => $this->configuration['show_change_plan_variant_button'],
    ];
    $form['show_cancel_plan_variant_button'] = [
      '#type' => 'checkbox',
      '#title' => $this->t('Show the "Cancel subscription" button'),
      '#default_value' => $this->configuration['show_cancel_plan_variant_button'],
    ];
    $form['show_components_subscribe_button'] = [
      '#type' => 'checkbox',
      '#title' => $this->t('Show the "Subscribe add-ons" button'),
      '#default_value' => $this->configuration['show_components_subscribe_button'],
    ];
    $form['show_components_unsubscribe_button'] = [
      '#type' => 'checkbox',
      '#title' => $this->t('Show the "Cancel add-on" button'),
      '#default_value' => $this->configuration['show_components_unsubscribe_button'],
    ];
    $form['modal_actions'] = [
      '#type' => 'checkbox',
      '#title' => $this->t('Open the actions in a modal'),
      '#default_value' => $this->configuration['modal_actions'],
    ];
    return $form;
  }

  /**
   * {@inheritdoc}
   */
  public function blockSubmit($form, FormStateInterface $form_state): void {
    $this->configuration['source'] = $form_state->getValue('source');
    $this->configuration['show_active_subscription'] = $form_state->getValue('show_active_subscription');
    $this->configuration['show_active_components'] = $form_state->getValue('show_active_components');
    $this->configuration['no_active_components_text'] = $form_state->getValue('no_active_components_text');
    $this->configuration['show_change_plan_variant_button'] = $form_state->getValue('show_change_plan_variant_button');
    $this->configuration['show_cancel_plan_variant_button'] = $form_state->getValue('show_cancel_plan_variant_button');
    $this->configuration['show_components_subscribe_button'] = $form_state->getValue('show_components_subscribe_button');
    $this->configuration['show_components_unsubscribe_button'] = $form_state->getValue('show_components_unsubscribe_button');
    $this->configuration['modal_actions'] = $form_state->getValue('modal_actions');
  }

  /**
   * {@inheritdoc}
   */
  public function build(): array {
    $userLocale = $this->languageManager->getCurrentLanguage()->getId();
    if (!$this->owner) {
      // We can't show anything, if there's no owner.
      return [];
    }

    // Show user information to prevent confusion. This is intended for the
    // current user!
    $build['current_user'] = [
      '#type' => 'item',
      '#title' => '<strong>' . $this->t('Username:') . '</strong>',
      '#markup' => $this->owner->getDisplayName(),
      '#wrapper_attributes' => ['class' => ['container-inline']],
    ];

    if ($this->currentUser->id() != $this->owner->id()) {
      $build['not_manageable'] = [
        'message' => [
          '#type' => 'inline_template',
          '#template' => '<div class="messages messages--warning"><div class="message">{{ "Managing other user accounts here is not yet implemented."|t }}</div></div>',
          '#context' => [],
        ],
      ];
      return $build;
    }

    $build['plan'] = [
      '#type' => 'details',
      '#title' => $this->t('My subscription plan'),
      '#open' => TRUE,
    ];

    // Show active subscriptions.
    if ($this->configuration['show_active_subscription']) {
      $activeBillwerkContract = $this->subscriber->getBillwerkContract();
      if (!empty($activeBillwerkContract)) {
        $activeBillwerkContractSubscription = $activeBillwerkContract->getBillwerkContractSubscription();
        $activeBillwerkPlanVariantId = $this->subscriber->getBillwerkContract()->getBillwerkContractSubscription()->getPlanVariantId();
        if (!empty($activeBillwerkPlanVariantId)) {
          $activeBillwerkPlanVariant = $this->billwerkEntitiesHelper->getBillwerkPlanVariantByPlanVariantId($activeBillwerkPlanVariantId, $this->environment, TRUE, FALSE);
          if (!empty($activeBillwerkPlanVariant)) {
            // List only known component entities:
            $build['plan']['active_plan_variant_subscription'] = [
              '#theme' => 'billwerk_plan_variant_subscription',
              // @improve: We might make the view mode configurable one day.
              '#billwerk_plan_variant' => $this->entityTypeManager->getViewBuilder('billwerk_plan_variant')->view($activeBillwerkPlanVariant, 'card'),
              '#id' => $activeBillwerkContract->getId(),
              '#planId' => $activeBillwerkContractSubscription->getPlanId(),
              '#phaseType' => $activeBillwerkContractSubscription->getPhaseType(),
              '#planVariantId' => $activeBillwerkContractSubscription->getPlanVariantId(),
              '#startDate' => $activeBillwerkContractSubscription->getStartDate(),
              '#endDate' => $activeBillwerkContractSubscription->getEndDate(),
              '#trialEndDate' => $activeBillwerkContractSubscription->getTrialEndDate(),
              '#billedUntil' => $activeBillwerkContractSubscription->getBilledUntil(),
              '#lastBillingDate' => $activeBillwerkContractSubscription->getLastBillingDate(),
              '#nextBillingDate' => $activeBillwerkContractSubscription->getNextBillingDate(),
              '#currency' => $activeBillwerkContractSubscription->getCurrency(),
              '#balance' => $activeBillwerkContractSubscription->getBalance(),
              '#userLocale' => $userLocale,
            ];
          }
        }
      }
    }

    if ($this->configuration['show_change_plan_variant_button'] || $this->configuration['show_cancel_plan_variant_button']) {
      $build['plan']['plan_variant_actions'] = [
        '#type' => 'actions',
        '#attributes' => [
          'class' => [
            'plan-actions',
          ],
        ],
      ];
    }
    if (!$this->subscriber->billwerkHasPendingContractPhases()) {
      // Change plan button:
      if ($this->configuration['show_change_plan_variant_button']) {
        $build['plan']['plan_variant_actions']['change_plan_variant_button'] = [
          '#type' => 'link',
          '#title' => $this->t('Change plan (Up- / downgrade)'),
          '#url' => Url::fromRoute('billwerk_subscriptions_manage.current_user_change_plan_variant'),
          '#attributes' => [
            'class' => [
              'button',
              'button--change-plan',
            ],
          ],
        ];
        if ($this->configuration['modal_actions']) {
          // Add modal classes and library, if enabled:
          $build['plan']['plan_variant_actions']['change_plan_variant_button']['#attributes']['class'][] = 'use-ajax';
          $build['plan']['plan_variant_actions']['change_plan_variant_button']['#attributes']['data-dialog-type'] = 'modal';
          $build['plan']['plan_variant_actions']['change_plan_variant_button']['#attributes']['data-dialog-options'] = Json::encode(['width' => '75%']);
          $build['plan']['plan_variant_actions']['change_plan_variant_button']['#attached']['library'][] = 'core/drupal.dialog.ajax';
        }
      }

      // Change plan button:
      if ($this->configuration['show_cancel_plan_variant_button']) {
        // Show button, if the plan isn't already the free one to downgrade to
        // or if this is a real Billwerk cancellation.
        if (($this->helper->getCancellationMethod() === SettingsForm::CANCELLATION_METHOD_DOWNGRADE
          && $this->helper->getBillwerkFreePlanVariantId() != $this->subscriber->getBillwerkContract()->getBillwerkContractSubscription()->getPlanVariantId())
          || ($this->helper->getCancellationMethod() === SettingsForm::CANCELLATION_METHOD_CANCEL_CONTRACT)) {
          $build['plan']['plan_variant_actions']['cancel_plan_variant_button'] = [
            '#type' => 'link',
            '#title' => $this->t('Cancel plan'),
            '#url' => Url::fromRoute('billwerk_subscriptions_manage.current_user_cancel_subscription'),
            '#attributes' => [
              'class' => [
                'button',
                'button--cancel-plan',
              ],
            ],
          ];
          if ($this->configuration['modal_actions']) {
            // Add modal classes and library, if enabled:
            $build['plan']['plan_variant_actions']['cancel_plan_variant_button']['#attributes']['class'][] = 'use-ajax';
            $build['plan']['plan_variant_actions']['cancel_plan_variant_button']['#attributes']['data-dialog-type'] = 'modal';
            $build['plan']['plan_variant_actions']['cancel_plan_variant_button']['#attributes']['data-dialog-options'] = Json::encode(['width' => '75%']);
            $build['plan']['plan_variant_actions']['cancel_plan_variant_button']['#attached']['library'][] = 'core/drupal.dialog.ajax';
          }
        }
      }
    }
    else {
      $build['plan']['plan_variant_actions']['not_manageable'] = [
        'message' => [
          '#type' => 'inline_template',
          '#template' => '<div class="messages messages--warning"><div class="message">{{ "You have pending contract changes. Further contract amendments are not possible until then. Please contact us to revoke cancellations or for individual cases."|t }}</div></div>',
          '#context' => [],
        ],
      ];
    }

    // Remove the plan if there are no elements visible:
    if (empty(Element::getVisibleChildren($build['plan']))) {
      unset($build['plan']); // @codingStandardsIgnoreLine
    }

    $build['components'] = [
      '#type' => 'details',
      '#title' => $this->t('My add-ons'),
      '#open' => TRUE,
    ];

    // Show active component subscriptions:
    if ($this->configuration['show_active_components']) {
      $activeBillwerkContract = $this->subscriber->getBillwerkContract();
      if (!empty($activeBillwerkContract)) {
        $activeBillwerkComponentSubscriptions = $activeBillwerkContract->getBillwerkContractSubscription()->getComponentSubscriptions();
        if (!empty($activeBillwerkComponentSubscriptions)) {
          $build['components']['active_component_subscriptions'] = [
            '#type' => 'container',
          ];

          foreach ($activeBillwerkComponentSubscriptions as $componentSubscriptionId => $subscriberCurrentComponentSubscription) {
            $componentEntity = $this->billwerkEntitiesHelper->getBillwerkComponentByComponentId($subscriberCurrentComponentSubscription->getComponentId(), $this->environment, TRUE, FALSE);
            if (!empty($componentEntity)) {
              // List only known component entities:
              $build['components']['active_component_subscriptions'][$componentSubscriptionId] = [
                '#theme' => 'billwerk_component_subscription',
                // @improve: We might make the view mode configurable one day.
                '#billwerk_component' => $this->entityTypeManager->getViewBuilder('billwerk_component')->view($componentEntity, 'card'),
                '#startDate' => $subscriberCurrentComponentSubscription->getStartDate(),
                '#endDate' => $subscriberCurrentComponentSubscription->getEndDate(),
                '#billedUntil' => $subscriberCurrentComponentSubscription->getBilledUntil(),
                '#quantity' => $subscriberCurrentComponentSubscription->getQuantity(),
                '#componentId' => $subscriberCurrentComponentSubscription->getComponentId(),
                '#id' => $subscriberCurrentComponentSubscription->getId(),
                '#userLocale' => $userLocale,
              ];
            }
          }
        }
        elseif (!empty($this->configuration['no_active_components_text']['value'])) {
          // Show empty text, if set:
          $build['components']['no_active_component_subscriptions'] = [
            '#type' => 'processed_text',
            '#text' => $this->configuration['no_active_components_text']['value'],
            '#format' => $this->configuration['no_active_components_text']['format'],
          ];
        }
      }
      else {
        $build['components']['no_active_billwerk_contract'] = [
          '#plain_text' => $this->t('No active contract'),
        ];
      }
    }

    if ($this->configuration['show_components_subscribe_button'] || $this->configuration['show_components_unsubscribe_button']) {
      $build['components']['components_actions'] = [
        '#type' => 'actions',
        '#attributes' => [
          'class' => [
            'component-actions',
          ],
        ],
      ];
    }

    // Subscribe component button:
    if ($this->configuration['show_components_subscribe_button']) {
      $build['components']['components_actions']['components_subscribe_button'] = [
        '#type' => 'link',
        '#title' => $this->t('Book add-ons'),
        '#url' => Url::fromRoute('billwerk_subscriptions_manage.current_user_components_subscribe'),
        '#attributes' => [
          'class' => [
            'button',
            'button--components-subscribe',
          ],
        ],
      ];
      if ($this->configuration['modal_actions']) {
        // Add modal classes and library, if enabled:
        $build['components']['components_actions']['components_subscribe_button']['#attributes']['class'][] = 'use-ajax';
        $build['components']['components_actions']['components_subscribe_button']['#attributes']['data-dialog-type'] = 'modal';
        $build['components']['components_actions']['components_subscribe_button']['#attributes']['data-dialog-options'] = Json::encode(['width' => '75%']);
        $build['components']['components_actions']['components_subscribe_button']['#attached']['library'][] = 'core/drupal.dialog.ajax';
      }
    }

    if ($this->configuration['show_components_unsubscribe_button'] && !empty($build['components']['active_component_subscriptions'])) {
      $build['components']['components_actions']['components_unsubscribe_button'] = [
        '#type' => 'link',
        '#title' => $this->t('Cancel add-ons'),
        '#url' => Url::fromRoute('billwerk_subscriptions_manage.current_user_components_unsubscribe'),
        '#attributes' => [
          'class' => [
            'button',
            'button--components-unsubscribe',
          ],
        ],
      ];
      if ($this->configuration['modal_actions']) {
        // Add modal classes and library, if enabled:
        $build['components']['components_actions']['components_unsubscribe_button']['#attributes']['class'][] = 'use-ajax';
        $build['components']['components_actions']['components_unsubscribe_button']['#attributes']['data-dialog-type'] = 'modal';
        $build['components']['components_actions']['components_unsubscribe_button']['#attributes']['data-dialog-options'] = Json::encode(['width' => '75%']);
        $build['components']['components_actions']['components_unsubscribe_button']['#attached']['library'][] = 'core/drupal.dialog.ajax';
      }
    }

    // Remove the components if there are no elements visible:
    if (empty(Element::getVisibleChildren($build['components']))) {
      unset($build['components']); // @codingStandardsIgnoreLine
    }

    // Cache by user.
    $build['#cache']['contexts'][] = 'user';

    // Add the cacheable dependencies:
    $this->renderer->addCacheableDependency($build, $this->owner);
    $this->renderer->addCacheableDependency($build, $this);

    return $build;
  }

  /**
   * {@inheritdoc}
   */
  protected function blockAccess(AccountInterface $account): AccessResult {
    if (!$this->owner) {
      return AccessResult::forbidden();
    }
    if ($account->id() === $this->owner->id()) {
      // Is the current users account.
      $access = AccessResult::allowedIfHasPermission($account, 'billwerk_subscriptions_selfservice_manage_own_contract');
    }
    else {
      // Is a different users account.
      $access = AccessResult::allowedIfHasPermission($account, 'billwerk_subscriptions_selfservice_manage_any_contract');
    }
    // The anonymous user may never be a subscriber!
    $hasUserBillwerkContractId = $this->owner->isAnonymous() ? FALSE : $this->subscriber->hasUserBillwerkContractId();
    return $access->andIf(AccessResult::allowedIf($hasUserBillwerkContractId));
  }

}

Главная | Обратная связь

drupal hosting | друпал хостинг | it patrol .inc