l10n_server-2.x-dev/l10n_community/src/Form/FilterForm.php
l10n_community/src/Form/FilterForm.php
<?php declare(strict_types=1); namespace Drupal\l10n_community\Form; use Drupal\Core\Form\FormBase; use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Language\LanguageInterface; use Drupal\Core\Url; use Drupal\l10n_community\Controller\L10nCommunityLanguagesController; /** * Translation export form. */ final class FilterForm extends FormBase { /** * {@inheritdoc} */ public function getFormId(): string { return 'l10n_community_filter'; } /** * {@inheritdoc} * * @param array $form * The form array. * @param \Drupal\Core\Form\FormStateInterface $form_state * The form_state object. * * @return array|string[] * The form array. * * @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException * @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException * @throws \Drupal\Core\TypedData\Exception\MissingDataException */ public function buildForm(array $form, FormStateInterface $form_state): array { $user_storage = \Drupal::entityTypeManager() ->getStorage('user'); $project_storage = \Drupal::entityTypeManager() ->getStorage('l10n_server_project'); $release_storage = \Drupal::entityTypeManager() ->getStorage('l10n_server_release'); $project_count = $project_storage ->getQuery() ->condition('status', 1) ->count()->execute(); /** @var \Drupal\group\Entity\Group $group */ $group = \Drupal::routeMatch()->getParameter('group'); $language = \Drupal::languageManager()->getLanguage($group->get('field_translation_language')->first()->getValue()['target_id']); $translation_options = [ L10N_STATUS_ALL => '<' . $this->t('Any') . '>', L10N_STATUS_UNTRANSLATED => $this->t('Untranslated'), L10N_STATUS_TRANSLATED => $this->t('Translated'), L10N_STATUS_IS_TRANSLATION => $this->t('Is translation'), ]; $suggestion_options = [ L10N_STATUS_ALL => '<' . $this->t('Any') . '>', L10N_STATUS_NO_SUGGESTION => $this->t('Has no suggestion'), L10N_STATUS_HAS_SUGGESTION => $this->t('Has suggestion'), L10N_STATUS_IS_SUGGESTION => $this->t('Is suggestion'), ]; // This form can be submitted multiple times to support AJAX flows. // So we set an initial default value set for the form values, if there was // none submitted already. Otherwise, the submitted values will take on // being the defaults. if (empty($form_state->getValues())) { $filters = L10nCommunityLanguagesController::buildFilterValues($_GET); $form_state->setValues([ 'project' => isset($filters['project']) ? $filters['project'] : '', 'release' => isset($filters['release']) ? $filters['release'] : 'all', 'context' => isset($filters['context']) ? $filters['context'] : 'all', 'status' => isset($filters['status']) ? $filters['status'] : L10N_STATUS_ALL, 'author' => isset($filters['author']) ? $filters['author'] : '', 'search' => isset($filters['search']) ? $filters['search'] : '', 'limit' => isset($filters['limit']) ? $filters['limit'] : 10, 'sid' => isset($filters['sid']) ? $filters['sid'] : 0, ]); } $form = [ '#prefix' => '<div class="l10n-server-filter clear-block">', '#suffix' => '</div>', ]; $class_is_default_project = empty($form_state->getValue('project')) ? 'is-default' : 'is-not-default'; if ($pid = $form_state->getValue('project')) { $selected_project = $project_storage->load($pid); } $form['project'] = [ '#title' => $this->t('Project'), '#default_value' => $selected_project ?? NULL, '#ajax' => [ 'callback' => '::ajaxReleases', 'event' => 'change', 'wrapper' => 'l10n-server-releases', 'effect' => 'fade', 'progress' => [ 'type' => 'throbber', 'message' => $this->t('Getting releases...'), ], ], '#attributes' => ['class' => [$class_is_default_project]], '#prefix' => '<div class="filter-widget">', '#suffix' => '</div>', ]; if ($project_count <= 30) { // Select widget for 1-30 projects. $form['project']['#type'] = 'select'; $form['project']['#options'] = ['all' => $this->t('All')]; $projects = $project_storage->loadMultiple(); /** @var \Drupal\l10n_server\Entity\L10nServerProjectInterface $project */ foreach ($projects as $project) { // URI used to shorten the lookup cycle in filter sanitization. $form['project']['#options'][$project->getUri()] = $project->label(); } } else { // Autocomplete field for more than 30 projects. $form['project'] += [ '#type' => 'entity_autocomplete', '#target_type' => 'l10n_server_project', '#size' => 20, ]; } // Always add releases option, so we can update it when // needed as the project changes with AJAX. $release_options = ['all' => $this->t('All')]; /** @var \Drupal\l10n_server\Entity\L10nServerProjectInterface $selected_project */ if (isset($selected_project)) { $releases = $release_storage->loadByProperties(['pid' => $selected_project->id()]); foreach ($releases as $release) { $release_options[$release->id()] = $release->label(); } } $class_is_default_release = $form_state->getValue('release') == 'all' ? 'is-default' : 'is-not-default'; $form['release'] = [ '#title' => $this->t('Release'), '#type' => 'select', '#options' => $release_options, '#default_value' => $form_state->getValue('release') ?? 'all', // Wrapper used to replace release options via AJAX. '#prefix' => '<div id="l10n-server-releases">', '#suffix' => '</div>', '#attributes' => ['class' => [$class_is_default_release]], ]; if (isset($selected_project) && $form_state->getValue('release') != 'all') { $form['release']['#field_suffix'] = '<div class="start-over-link"><a href="' . Url::fromUri('internal:/group/' . $group->id() . '/reset/' . $selected_project->getUri() . '/' . $form_state->getValue('release'))->toString() . '">' . t('Missing strings?') . '</a></div>'; } if (count($contexts = $this->getContexts()) > 1) { $class_is_default_context = $form_state->getValue('context') == 'all' ? 'is-default' : 'is-not-default'; $form['context'] = [ '#title' => $this->t('Context'), '#type' => 'select', '#options' => ['all' => $this->t('All')] + $contexts, '#default_value' => $form_state->getValue('context'), '#attributes' => ['class' => [$class_is_default_context]], '#prefix' => '<div class="break"></div><div class="filter-widget">', '#suffix' => '</div><div class="break"></div>', ]; } $form['reveal_more_filter'] = [ '#title' => $this->t('Reveal more filters'), '#type' => 'radio', '#states' => [ 'visible' => [ ':input[name="reveal_more_filter"]' => [ 'checked' => FALSE, ], ], ], ]; $form['status'] = [ '#type' => 'container', '#tree' => TRUE, '#prefix' => '<div class="' . ($form_state->getValue('status') == L10N_STATUS_ALL ? 'is-default' : 'is-not-default') . '">', '#suffix' => '</div>', '#attributes' => ['class' => ['status-wrapper']], '#states' => [ 'visible' => [ ':input[name="reveal_more_filter"]' => [ 'checked' => TRUE, ], ], ], ]; $form['status']['translation'] = [ '#title' => $this->t('Status'), '#type' => 'select', '#options' => $translation_options, '#default_value' => $form_state->getValue('status') || (L10N_STATUS_TRANSLATED | L10N_STATUS_UNTRANSLATED | L10N_STATUS_IS_TRANSLATION), '#prefix' => '<div class="filter-widget">', ]; $form['status']['suggestion'] = [ '#type' => 'select', '#options' => $suggestion_options, '#default_value' => $form_state->getValue('status') || (L10N_STATUS_HAS_SUGGESTION | L10N_STATUS_NO_SUGGESTION | L10N_STATUS_IS_SUGGESTION), '#suffix' => '</div>', ]; $class_is_default_author = empty($form_state->getValue('author')) ? 'is-default' : 'is-not-default'; if ($uid = $form_state->getValue('author')) { $selected_author = $user_storage->load($uid); } $form['author'] = [ '#type' => 'entity_autocomplete', '#title' => $this->t('Submitted by'), '#maxlength' => 60, '#target_type' => 'user', '#default_value' => $selected_author ?? NULL, '#size' => 15, '#attributes' => ['class' => [$class_is_default_author]], '#prefix' => '<div class="filter-widget">', '#suffix' => '</div>', '#states' => [ 'visible' => [ ':input[name="reveal_more_filter"]' => [ 'checked' => TRUE, ], ], ], ]; $class_is_default_search = empty($form_state->getValue('search')) ? 'is-default' : 'is-not-default'; $form['search'] = [ '#title' => $this->t('Contains'), '#type' => 'textfield', '#default_value' => $form_state->getValue('search'), '#size' => 20, '#attributes' => ['class' => [$class_is_default_search]], '#prefix' => '<div class="filter-widget">', '#suffix' => '</div><div class="break"></div>', '#states' => [ 'visible' => [ ':input[name="reveal_more_filter"]' => [ 'checked' => TRUE, ], ], ], ]; $class_is_default_sid = empty($form_state->getValue('sid')) ? 'is-default' : 'is-not-default'; $form['sid'] = [ '#title' => t('String ID'), '#type' => 'textfield', '#default_value' => empty($form_state->getValue('sid')) ? '' : $form_state->getValue('sid'), '#size' => 10, '#attributes' => ['class' => [$class_is_default_sid]], '#prefix' => '<div class="filter-widget">', '#suffix' => '</div>', '#states' => [ 'visible' => [ ':input[name="reveal_more_filter"]' => [ 'checked' => TRUE, ], ], ], ]; $class_is_default_limit = $form_state->getValue('limit') == 10 ? 'is-default' : 'is-not-default'; $form['limit'] = [ '#type' => 'select', '#title' => t('Limit'), '#options' => [5 => 5, 10 => 10, 20 => 20, 30 => 30, 50 => 50], '#default_value' => $form_state->getValue('limit'), '#attributes' => ['class' => [$class_is_default_limit]], '#display_none' => $form_state->getValue('limit') == 10, '#prefix' => '<div class="filter-widget">', '#suffix' => '</div>', '#states' => [ 'visible' => [ ':input[name="reveal_more_filter"]' => [ 'checked' => TRUE, ], ], ], ]; $form['actions'] = [ '#type' => 'actions', '#prefix' => '<div class="filter-submit">', '#suffix' => '</div>', ]; $form['actions']['submit'] = [ '#type' => 'submit', '#name' => 'submit', '#value' => $this->t('Filter'), '#button_type' => 'primary', ]; $form['actions']['reset'] = [ '#type' => 'markup', '#markup' => '<div class="reset-link"><a href="' . Url::fromUri('internal:/group/' . $group->id() . '/translate')->toString() . '" class="filter-exclude">' . $this->t('Clear all filters') . '</a></div>', ]; $form['#attached']['library'][] = 'l10n_community/forms'; $form['#attached']['library'][] = 'l10n_community/editor'; if ($language->getDirection() == LanguageInterface::DIRECTION_RTL) { $form['#attached']['library'][] = 'l10n_community/editor-rtl'; } return $form; } /** * {@inheritdoc} */ public function validateForm(array &$form, FormStateInterface $form_state) { } /** * {@inheritdoc} */ public function submitForm(array &$form, FormStateInterface $form_state) { $trigger = $form_state->getTriggeringElement(); if ($trigger['#name'] === 'reset') { // Redirect clearing the filters. $route_name = \Drupal::routeMatch()->getRouteName(); $route_parameters = [ 'group' => \Drupal::routeMatch()->getParameter('group')->id(), ]; $form_state->setRedirect($route_name, $route_parameters); } if ($trigger['#name'] === 'submit') { // Redirect keeping the relevant filters intact in the URL. $route_name = \Drupal::routeMatch()->getRouteName(); $route_parameters = [ 'group' => \Drupal::routeMatch()->getParameter('group')->id(), ]; $options = [ 'query' => L10nCommunityLanguagesController::buildFilterValues([ 'project' => $form_state->getValue('project'), 'status' => $form_state->getValue('status'), 'release' => $form_state->getValue('release'), 'search' => $form_state->getValue('search'), 'author' => $form_state->getValue('author'), 'context' => $form_state->getValue('context'), 'limit' => $form_state->getValue('limit'), 'sid' => $form_state->getValue('sid'), ]), ]; $form_state->setRedirect($route_name, $route_parameters, $options); } } /** * Get all contexts from the database. * * @return array * Array of context values. */ private function getContexts() { $contexts = []; $result = \Drupal::database() ->query("SELECT DISTINCT context FROM {l10n_server_string} ORDER BY context"); foreach ($result as $context) { $contexts[empty($context->context) ? 'none' : $context->context] = empty($context->context) ? $this->t('No context') : $context->context; } return $contexts; } /** * Ajax callback. * * @param array $form * The form array. * @param \Drupal\Core\Form\FormStateInterface $form_state * The form_state object. * * @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException * @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException */ public static function ajaxReleases(array &$form, FormStateInterface $form_state) { $release_options = []; if ($pid = $form_state->getValue('project')) { $releases = \Drupal::entityTypeManager() ->getStorage('l10n_server_release') ->loadByProperties(['pid' => $pid]); foreach ($releases as $release) { $release_options[$release->id()] = t('@version only', [ '@version' => $release->getVersion(), ]); } krsort($release_options); } $release_options = ['all' => t('All')] + $release_options; $form['release']['#options'] = $release_options; return $form['release']; } }