l10n_server-2.x-dev/l10n_community/src/Controller/L10nCommunityProjectsController.php
l10n_community/src/Controller/L10nCommunityProjectsController.php
<?php declare(strict_types=1); namespace Drupal\l10n_community\Controller; use Drupal\Core\Config\ConfigFactoryInterface; use Drupal\Core\Config\ConfigManagerInterface; use Drupal\Core\Config\ImmutableConfig; use Drupal\Core\Controller\ControllerBase; use Drupal\Core\Database\Connection; use Drupal\Core\Routing\RouteMatchInterface; use Drupal\Core\StringTranslation\TranslatableMarkup; use Drupal\Core\Url; use Drupal\l10n_community\L10nStatistics; use Drupal\l10n_server\Entity\L10nServerProjectInterface; use Drupal\l10n_server\L10nProjectManager; use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\HttpFoundation\RequestStack; /** * Returns responses for Localization community UI routes. */ final class L10nCommunityProjectsController extends ControllerBase { /** * Community settings. * * @var \Drupal\Core\Config\ImmutableConfig */ private ImmutableConfig $communitySettings; /** * The controller constructor. * * @param \Drupal\Core\Database\Connection $connection * The database connection. * @param \Drupal\Core\Config\ConfigManagerInterface $configManager * The config manager. * @param \Drupal\Core\Routing\RouteMatchInterface $routeMatch * The route match. * @param \Symfony\Component\HttpFoundation\RequestStack $requestStack * The request stack. * @param \Drupal\l10n_community\L10nStatistics $statistics * The l10n_community.statistics service. * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory * The config factory. * @param \Drupal\l10n_server\L10nProjectManager $l10nProjectManager * The l10n project manager service. */ public function __construct( private readonly Connection $connection, private readonly ConfigManagerInterface $configManager, private readonly RouteMatchInterface $routeMatch, private readonly RequestStack $requestStack, private readonly L10nStatistics $statistics, ConfigFactoryInterface $config_factory, private readonly L10nProjectManager $l10nProjectManager, ) { $this->communitySettings = $config_factory->get('l10n_community.settings'); } /** * {@inheritdoc} */ public static function create( ContainerInterface $container, ): self { return new L10nCommunityProjectsController( $container->get('database'), $container->get('config.manager'), $container->get('current_route_match'), $container->get('request_stack'), $container->get('l10n_community.statistics'), $container->get('config.factory'), $container->get('l10n_server.project_manager'), ); } /** * Title callback. * * @return \Drupal\Core\StringTranslation\TranslatableMarkup * Translatable markup object. */ public function title(): TranslatableMarkup { $project = $this->routeMatch->getParameter('project'); if ($project instanceof L10nServerProjectInterface) { return $this->t('@project_name releases', [ '@project_name' => $project->label(), ]); } return $this->t('Releases'); } /** * Builds the response. */ public function explore(): array { if ( !$this->communitySettings->get('stats_enabled') || !$this->communitySettings->get('stats_explore_projects_enabled') ) { $build['content'] = [ '#type' => 'item', '#markup' => $this->t('Statistics for projects have been disabled.'), ]; return [ 'content' => [ '#type' => 'item', '#markup' => $this->t('Statistics for projects have been disabled.'), ], ]; } $options = ['pager' => $this->communitySettings->get('project_per_page')]; if (!is_null($this->requestStack->getCurrentRequest()->get('initial'))) { $options['initial'] = strtolower($this->requestStack->getCurrentRequest()->get('initial')); } if (!$projects = $this->getProjects($options)) { $build['content'] = [ '#type' => 'item', '#markup' => $this->t('No projects found.'), ]; return $build; } $projects_ids = array_map(function ($project) { return $project->pid; }, $projects); $language_count = count($this->languageManager()->getLanguages()); $string_counts = $this->statistics->getProjectsStringCount(project_ids: array_values($projects_ids)); $table = []; foreach ($projects as $project) { $total_strings = ($string_counts[$project->pid]['count'] ?? 0 * $language_count); $string_translated = $string_counts[$project->pid]['translations'] ?? 0; $string_suggestions = $string_counts[$project->pid]['suggestions'] ?? 0; $table[] = [ [ 'data' => $this->t('<a href="@url">@project</a>', [ '@project' => $project->title, '@url' => Url::fromUri('internal:/translate/projects/' . $project->pid)->toString(), ]), 'class' => ['rowhead'], ], [ 'data' => [ '#theme' => 'l10n_community_progress_columns', '#sum' => $total_strings, '#translated' => $string_translated, ], ], ]; } $build['pre_pager'] = [ '#type' => 'pager', ]; $build['content'] = [ '#type' => 'table', '#header' => [ t('Project'), t('Progress'), ], '#rows' => $table, '#attributes' => [ 'class' => [ 'l10n-community-overview', ], ], '#cache' => [ // @todo Fix cache handling. // 'max-age' => Cache::PERMANENT, ], ]; $build['post_pager'] = [ '#type' => 'pager', ]; $build['#attached']['library'][] = 'l10n_community/tables'; return $build; } /** * Builds the response. * * @param \Drupal\l10n_server\Entity\L10nServerProjectInterface $project * The project entity. * * @return array * A render array. */ public function overview(L10nServerProjectInterface $project): array { $build['content'] = [ '#type' => 'item', '#markup' => __METHOD__ . '::' . $project->label(), ]; return $build; } /** * Builds the response. * * @param \Drupal\l10n_server\Entity\L10nServerProjectInterface $project * The project entity. * * @return array * A render array. */ public function export(L10nServerProjectInterface $project): array { $build['content'] = [ '#type' => 'item', '#markup' => __METHOD__ . '::' . $project->label(), ]; return $build; } /** * Builds the response. * * @param \Drupal\l10n_server\Entity\L10nServerProjectInterface $project * The project entity. * * @return array * A render array. */ public function releases(L10nServerProjectInterface $project): array { $build['content'] = [ '#type' => 'item', '#markup' => __METHOD__ . '::' . $project->label(), ]; return $build; } /** * Provides a list of projects from the database, ordered by uri. * * @param array $options * Associative array of options * - 'uri': Project URI, if requesting information about one project only. * If not specified, information about all projects is returned. * - 'pager': Number of projects to return a pager query result with. If * NULL, no pager is used. * - 'all': If not specified, unpublished projects are excluded (default). * If TRUE, even unpublished projects are returned (for admin pages). * * @return array * An associative array keyed with project uris. * * @throws \Exception */ private function getProjects(array $options = []): array { static $projects = []; $select = $this->connection ->select('l10n_server_project', 'p') ->fields('p', []); // Consider returning all projects or just published ones. if (empty($options['all'])) { $select->condition('status', 1); } if (!empty($options['pager'])) { if (!empty($options['initial'])) { $initials = $this->l10nProjectManager->getProjectInitials(); if (isset($initials[$options['initial']])) { $args = $initials[$options['initial']]['values']; for ($i = 0; $i < count($args); $i++) { $arguments[':p_' . $i] = $args[$i]; } $placeholders = implode(',', array_keys($arguments)); $select->where("SUBSTRING(title, 1, 1) IN ($placeholders)", $arguments); } } // If a pager view was asked for, collect data independently. $select->orderBy('title'); $result = $select ->extend('Drupal\Core\Database\Query\PagerSelectExtender') ->limit($options['pager']) ->execute(); $pager_results = $result ->fetchAllAssoc('uri'); // Save project information for later, if someone asks for it by uri. $projects = array_merge($projects, $pager_results); return $pager_results; } else { if (isset($options['uri'])) { // A specific project was asked for. if (isset($projects[$options['uri']])) { // Can be served from the local cache. return $projects[$options['uri']]; } // Not found in cache, so query and cache before returning. $result = $this->connection ->query("SELECT * FROM {l10n_server_project} WHERE uri = :uri", [ ':uri' => $options['uri'], ]); if ($project = $result->fetchObject()) { $projects[$options['uri']] = $project; return $project; } } else { // A list of *all* projects was asked for. $results = $select->orderBy('uri')->execute(); foreach ($results as $project) { $projects[$project->uri] = $project; } return $projects; } } return $projects; } }