From be96a29430683bd0b5cf14955329b0572a752d94 Mon Sep 17 00:00:00 2001 From: orakili Date: Fri, 10 Feb 2023 11:26:06 +0000 Subject: [PATCH 1/6] feat: better distinction between rivers, sub rivers etc. Refs: RW-700 --- .../reliefweb_entities/src/DocumentTrait.php | 2 +- .../reliefweb_entities/src/Entity/Topic.php | 100 ++--- .../src/Entity/Training.php | 2 +- .../src/SectionedContentTrait.php | 25 +- .../FieldWidget/ReliefWebSectionLinks.php | 10 +- .../src/Controller/Homepage.php | 2 +- .../reliefweb_meta/reliefweb_meta.module | 104 ++---- .../reliefweb_rivers/js/advanced-search.js | 4 +- .../reliefweb_rivers/src/AdvancedSearch.php | 7 +- .../src/Controller/SearchConverter.php | 61 +-- .../reliefweb_rivers/src/Parameters.php | 66 +++- .../reliefweb_rivers/src/RiverServiceBase.php | 350 ++++++++++++++++-- .../src/RiverServiceInterface.php | 139 ++++++- .../src/Services/BlogPostRiver.php | 14 + .../src/Services/CountryRiver.php | 9 +- .../src/Services/DisasterRiver.php | 11 +- .../src/Services/JobRiver.php | 22 +- .../src/Services/ReportRiver.php | 41 +- .../src/Services/SourceRiver.php | 32 +- .../src/Services/TopicRiver.php | 16 +- .../src/Services/TrainingRiver.php | 31 +- 21 files changed, 763 insertions(+), 285 deletions(-) diff --git a/html/modules/custom/reliefweb_entities/src/DocumentTrait.php b/html/modules/custom/reliefweb_entities/src/DocumentTrait.php index 9910b1543..6bb8da3f5 100644 --- a/html/modules/custom/reliefweb_entities/src/DocumentTrait.php +++ b/html/modules/custom/reliefweb_entities/src/DocumentTrait.php @@ -298,7 +298,7 @@ public function getEntityMetaFromField($field, $code = NULL, array $extra_fields if ($code !== NULL) { $url = RiverServiceBase::getRiverUrl($this->bundle(), [ 'advanced-search' => '(' . $code . $entity->id() . ')', - ]); + ], $entity->label(), TRUE); } else { $url = $entity->toUrl(); diff --git a/html/modules/custom/reliefweb_entities/src/Entity/Topic.php b/html/modules/custom/reliefweb_entities/src/Entity/Topic.php index 95a9ac785..9a14862bc 100644 --- a/html/modules/custom/reliefweb_entities/src/Entity/Topic.php +++ b/html/modules/custom/reliefweb_entities/src/Entity/Topic.php @@ -14,8 +14,6 @@ use Drupal\reliefweb_entities\SectionedContentTrait; use Drupal\reliefweb_revisions\EntityRevisionedInterface; use Drupal\reliefweb_revisions\EntityRevisionedTrait; -use Drupal\reliefweb_rivers\AdvancedSearch; -use Drupal\reliefweb_rivers\Parameters; use Drupal\reliefweb_rivers\RiverServiceBase; use Drupal\reliefweb_utility\Helpers\MediaHelper; @@ -231,7 +229,8 @@ public function getIcon() { * Convert a river URL to API payload. * * @param array $data - * Array with the river URL, title and optional override. + * Array with the river URL, title and optional override, limit, view + * and exclude properties. * @param int $limit * Number of resources to retrieve. * @@ -250,103 +249,54 @@ protected function riverUrlToApi(array $data, $limit = 3) { $url = $data['url']; $title = $data['title']; $override = $data['override'] ?? NULL; + $limit = $data['limit'] ?? $limit; - $info = RiverServiceBase::getRiverServiceFromUrl($url); - if (empty($info)) { + $service = RiverServiceBase::getRiverServiceFromUrl($url); + if (empty($service)) { return []; } - // Extract the query part from the URL. - parse_str(parse_url($url, PHP_URL_QUERY), $query); - - // Parse the query parameters. - $parameters = new Parameters($query); - - // Service and river information. - $service = $info['service']; - $resource = $service->getResource(); - $bundle = $service->getBundle(); - $river = $service->getRiver(); - - // Get the river view. - $view = $data['view'] ?? $info['view'] ?? $parameters->getString('view'); - $views = $service->getViews(); - $view = isset($views[$view]) ? $view : $service->getDefaultView(); - if ($view === $service->getDefaultView()) { - $parameters->remove('view'); - } - else { - $parameters->set('view', $view); + // Update the river view. + if (isset($data['view'])) { + $service->setSelectedView($data['view']); } - // Get the advanced search handler. - $advanced_search = new AdvancedSearch( - $bundle, - $river, - $parameters, - $service->getFilters(), - $service->getFilterSample() - ); - - // Get the sanitized search parameter. - $search = $parameters->getString('search'); - - // Get the API payload for the river and set the limit. - $payload = $service->getApiPayload($view); - $payload['limit'] = $data['limit'] ?? $limit; + // Get the API payload ready for the API request. + $payload = $service->prepareApiRequest($limit, FALSE); // If an override is defined we add a search condition on the override ID // with a boost and sort by score first. This will results in the override // document to appear first if it exists and keep the rest of the documents // in the proper order. if (!empty($override) && is_numeric($override)) { - if (!empty($search)) { - $search = 'id:' . $override . ' OR (' . $search . ')'; + if (!empty($payload['query']['value'])) { + $payload['query']['value'] = 'id:' . $override . ' OR (' . $payload['query']['value'] . ')'; } else { - $search = 'id:' . $override . '^1000 OR id:>0'; + $payload['query']['value'] = 'id:' . $override . '^1000 OR id:>0'; } array_unshift($payload['sort'], 'score:desc'); } - // Set the full text search query or remove it if empty. - if (!empty($search)) { - $payload['query']['value'] = $search; - } - else { - unset($payload['query']); - } - - // Generate the API filter with the facet and advanced search filters. - $filter = $advanced_search->getApiFilter(); - if (!empty($filter)) { - // Update the payload filter. - if (!empty($payload['filter'])) { - $payload['filter'] = [ - 'conditions' => [ - $payload['filter'], - $filter, - ], - 'operator' => 'AND', - ]; - } - else { - $payload['filter'] = $filter; - } - } - return [ - 'resource' => $resource, - 'bundle' => $bundle, + 'resource' => $service->getResource(), + 'bundle' => $service->getBundle(), 'entity_type' => $service->getEntityTypeId(), - 'river' => $river, + 'river' => $service->getRiver(), 'title' => $title, - 'view' => $view, + 'view' => $service->getSelectedView(), 'payload' => $payload, 'exclude' => $data['exclude'] ?? [], // Create a sanitized version of the given URL for the view more. 'more' => [ - 'url' => RiverServiceBase::getRiverUrl($bundle, $parameters->getAll()), + 'url' => RiverServiceBase::getRiverUrl( + $service->getBundle(), + $service->getParameters()->getAllSorted(['list']) + // @todo after reviewing the impact of adding the list parameter to + // river URL we may want to revisit what to do with the ones used + // in topics. For now no title because it's too random. + // @see RW-700, RW-706 + ), 'label' => $this->t('View all @title', [ '@title' => $title, ]), diff --git a/html/modules/custom/reliefweb_entities/src/Entity/Training.php b/html/modules/custom/reliefweb_entities/src/Entity/Training.php index 0cec5e710..69ce1bb69 100644 --- a/html/modules/custom/reliefweb_entities/src/Entity/Training.php +++ b/html/modules/custom/reliefweb_entities/src/Entity/Training.php @@ -77,7 +77,7 @@ public function getEntityMeta() { 'name' => $this->field_cost->value, 'url' => RiverServiceBase::getRiverUrl($this->bundle(), [ 'advanced-search' => '(CO' . $this->field_cost->value . ')', - ]), + ], $this->field_cost->value === 'free' ? $this->t('Free') : $this->t('Fee-based'), TRUE), ]; } diff --git a/html/modules/custom/reliefweb_entities/src/SectionedContentTrait.php b/html/modules/custom/reliefweb_entities/src/SectionedContentTrait.php index 5fa0387be..685568066 100644 --- a/html/modules/custom/reliefweb_entities/src/SectionedContentTrait.php +++ b/html/modules/custom/reliefweb_entities/src/SectionedContentTrait.php @@ -204,7 +204,9 @@ public function getKeyContentApiQuery($code = 'PC', $limit = 3) { 'more' => [ 'url' => RiverServiceBase::getRiverUrl('report', [ 'advanced-search' => '(' . $code . $this->id() . ')_(F10)', - ]), + ], $this->t('@label Situation Reports', [ + '@label' => $this->label(), + ])), 'label' => $this->t('View all @label Situation Reports', [ '@label' => $this->label(), ]), @@ -260,7 +262,9 @@ public function getAppealsResponsePlansApiQuery($code = 'PC', $limit = 50) { 'more' => [ 'url' => RiverServiceBase::getRiverUrl('report', [ 'advanced-search' => '(' . $code . $this->id() . ')_(F4)', - ]), + ], $this->t('@label Appeals and Reponse Plans', [ + '@label' => $this->label(), + ])), 'label' => $this->t('View all @label Appeals and Response Plans', [ '@label' => $this->label(), ]), @@ -325,7 +329,7 @@ public function getMostReadApiQuery($code = 'PC', $limit = 5) { 'more' => [ 'url' => RiverServiceBase::getRiverUrl('report', [ 'advanced-search' => '(' . $code . $entity_id . ')', - ]), + ], $this->label(), TRUE), 'label' => $this->t('View all @label Updates', [ '@label' => $this->label(), ]), @@ -363,7 +367,7 @@ public function getLatestUpdatesApiQuery($code = 'PC', $limit = 3) { 'more' => [ 'url' => RiverServiceBase::getRiverUrl('report', [ 'advanced-search' => '(' . $code . $entity_id . ')', - ]), + ], $this->label(), TRUE), 'label' => $this->t('View all @label Updates', [ '@label' => $this->label(), ]), @@ -410,8 +414,11 @@ public function getLatestMapsInfographicsApiQuery($code = 'PC', $limit = 3) { // Link to the updates river with the maps/infographics for the entity. 'more' => [ 'url' => RiverServiceBase::getRiverUrl('report', [ - 'advanced-search' => '(' . $code . $entity_id . ')_(F12.F12570)', - ]), + 'view' => 'maps', + 'advanced-search' => '(' . $code . $entity_id . ')', + ], $this->t('@label Maps and Infographics', [ + '@label' => $this->label(), + ])), 'label' => $this->t('View all @label Maps and Infographics', [ '@label' => $this->label(), ]), @@ -444,7 +451,7 @@ public function getLatestJobsApiQuery($code = 'C', $limit = 3) { 'more' => [ 'url' => RiverServiceBase::getRiverUrl('job', [ 'advanced-search' => '(' . $code . $entity_id . ')', - ]), + ], $this->label(), TRUE), 'label' => $this->t('View all @label Jobs', [ '@label' => $this->label(), ]), @@ -477,7 +484,7 @@ public function getLatestTrainingApiQuery($code = 'C', $limit = 3) { 'more' => [ 'url' => RiverServiceBase::getRiverUrl('training', [ 'advanced-search' => '(' . $code . $entity_id . ')', - ]), + ], $this->label(), TRUE), 'label' => $this->t('View all @label Training Opportunities', [ '@label' => $this->label(), ]), @@ -521,7 +528,7 @@ public function getLatestDisastersApiQuery($code = 'C', $limit = 100) { 'more' => [ 'url' => RiverServiceBase::getRiverUrl('disaster', [ 'advanced-search' => '(' . $code . $entity_id . ')', - ]), + ], $this->label(), TRUE), 'label' => $this->t('View all @label Disasters', [ '@label' => $this->label(), ]), diff --git a/html/modules/custom/reliefweb_fields/src/Plugin/Field/FieldWidget/ReliefWebSectionLinks.php b/html/modules/custom/reliefweb_fields/src/Plugin/Field/FieldWidget/ReliefWebSectionLinks.php index 1edeb36b9..6afea0920 100644 --- a/html/modules/custom/reliefweb_fields/src/Plugin/Field/FieldWidget/ReliefWebSectionLinks.php +++ b/html/modules/custom/reliefweb_fields/src/Plugin/Field/FieldWidget/ReliefWebSectionLinks.php @@ -259,8 +259,8 @@ public static function parseLinkData(array &$item, array $settings = []) { return t('Invalid configuration: no river allowed.'); } - $info = RiverServiceBase::getRiverServiceFromUrl($item['url']); - if (empty($info) || !isset($allowed_rivers[$info['bundle']])) { + $service = RiverServiceBase::getRiverServiceFromUrl($item['url']); + if (!isset($service) || !isset($allowed_rivers[$service->getBundle()])) { $allowed_rivers = array_intersect_key(ReliefWebSectionLinksFieldType::getAllowedRivers(), $allowed_rivers); return t('Invalid URL: use a link to one of the following rivers: @rivers.', [ '@rivers' => implode(', ', $allowed_rivers), @@ -275,7 +275,7 @@ public static function parseLinkData(array &$item, array $settings = []) { } $entity_type = \Drupal::entityTypeManager() - ->getStorage($info['service']->getEntityTypeId()) + ->getStorage($service->getEntityTypeId()) ->getEntityType(); $table = $entity_type->getDataTable(); @@ -296,9 +296,9 @@ public static function parseLinkData(array &$item, array $settings = []) { return t('Invalid override: the document was not found.'); } // Check that the document's type matches the river bundle. - elseif ($result->{$bundle_key} !== $info['bundle']) { + elseif ($result->{$bundle_key} !== $service->getBundle()) { return t('Invalid override: the document is not a @bundle.', [ - '@bundle' => $info['bundle'], + '@bundle' => $service->getBundle(), ]); } // Only published documents are valid. diff --git a/html/modules/custom/reliefweb_homepage/src/Controller/Homepage.php b/html/modules/custom/reliefweb_homepage/src/Controller/Homepage.php index a3de2dd7e..4e4919bf4 100644 --- a/html/modules/custom/reliefweb_homepage/src/Controller/Homepage.php +++ b/html/modules/custom/reliefweb_homepage/src/Controller/Homepage.php @@ -251,7 +251,7 @@ public function getHeadlinesApiPayload($limit = 8) { 'more' => [ 'url' => RiverServiceBase::getRiverUrl('report', [ 'view' => 'headlines', - ]), + ], $this->t('Updates (Headlines)')), 'label' => $this->t('View all headlines'), ], ]; diff --git a/html/modules/custom/reliefweb_meta/reliefweb_meta.module b/html/modules/custom/reliefweb_meta/reliefweb_meta.module index 54c323b09..1092a60a1 100644 --- a/html/modules/custom/reliefweb_meta/reliefweb_meta.module +++ b/html/modules/custom/reliefweb_meta/reliefweb_meta.module @@ -52,79 +52,47 @@ function reliefweb_meta_metatags_alter(array &$metatags, array &$context) { 'image' => reliefweb_meta_get_image_url(), ]; + // River pages. if (strpos($route_name, 'reliefweb_rivers.') === 0) { try { - $description = \Drupal::service($route_name)->getRiverDescription(); + // The river services have the same name as the corresponding route name. + $river_service = \Drupal::service($route_name); + $data['title'] = $river_service->getPageTitle(); + $data['description'] = $river_service->getRiverDescription(); + $data['canonical'] = $river_service->getCanonicalUrl(); + unset($data['shortlink']); } catch (Exception $exception) { - // Nothing to do. + // Nothing to do, that should not happen. } } - - switch ($route_name) { - case 'entity.node.canonical': - $data = reliefweb_meta_get_node_metatags($context['entity']); - break; - - case 'entity.taxonomy_term.canonical': - $data = reliefweb_meta_get_term_metatags($context['entity']); - break; - - case 'reliefweb_homepage.homepage.page': - $data['type'] = 'website'; - $data['description'] = \Drupal::config('system.site')->get('slogan') ?? ''; - // Change the metatags's title directly to avoid adding the "| Reliefweb" - // suffix in reliefweb_meta_update_metatags(). - $metatags['title'] = t('@site_name - Informing humanitarians worldwide', [ - '@site_name' => \Drupal::config('system.site')->get('name') ?? 'ReliefWeb', - ]); - - break; - - case 'reliefweb_rivers.blog_post.river': - $data['title'] = t('Blog'); - $data['description'] = $description ?? t("A look at the ideas and projects we're working on as we strive to grow and improve ReliefWeb."); - break; - - case 'reliefweb_rivers.country.river': - $data['title'] = t('Countries'); - $data['description'] = $description ?? t('ReliefWeb country pages provide a comprehensive overview of the humanitarian situation and feature situation reports, news and press releases, assessments, evaluations, infographics, maps and more.'); - break; - - case 'reliefweb_rivers.disaster.river': - $data['title'] = t('Disasters'); - $data['description'] = $description ?? t('ReliefWeb disaster pages provide an overview of the situation and situation reports, news and press releases, assessments, evaluations, infographics and maps. Browse our list of natural disasters with humanitarian impact from 1981 until today.'); - break; - - case 'reliefweb_rivers.job.river': - $data['title'] = t('Jobs'); - $data['description'] = $description ?? t('Your gateway for humanitarian and development jobs. Search and/or drill down with filters to narrow down the listings.'); - break; - - case 'reliefweb_rivers.report.river': - $data['title'] = t('Updates'); - $data['description'] = $description ?? t('Your gateway to all content to date. Search and/or drill down with filters to narrow down the content.'); - break; - - case 'reliefweb_rivers.source.river': - $data['title'] = t('Organizations'); - $data['description'] = $description ?? t('A list of organizations that are actively providing ReliefWeb with content (reports, jobs and training).'); - break; - - case 'reliefweb_rivers.topic.river': - $data['title'] = t('Topics'); - $data['description'] = $description ?? t('Curated pages dedicated to humanitarian themes and specific humanitarian crises.'); - break; - - case 'reliefweb_rivers.training.river': - $data['title'] = t('Training'); - $data['description'] = $description ?? t('Your gateway for humanitarian training opportunities. Search and/or drill down with filters to narrow down the listings.'); - break; - - default: - // Not so great to re-use the same description but normally all the - // main and meaningful pages are covered above or are nodes or terms. - $data['description'] = \Drupal::config('system.site')->get('slogan') ?? ''; + // Other pages. + else { + switch ($route_name) { + case 'entity.node.canonical': + $data = reliefweb_meta_get_node_metatags($context['entity']); + break; + + case 'entity.taxonomy_term.canonical': + $data = reliefweb_meta_get_term_metatags($context['entity']); + break; + + case 'reliefweb_homepage.homepage.page': + $data['type'] = 'website'; + $data['description'] = \Drupal::config('system.site')->get('slogan') ?? ''; + // Change the metatags's title directly so that the "| ReliefWeb" + // suffix is not added in reliefweb_meta_update_metatags(). + $metatags['title'] = t('@site_name - Informing humanitarians worldwide', [ + '@site_name' => \Drupal::config('system.site')->get('name') ?? 'ReliefWeb', + ]); + + break; + + default: + // Not so great to re-use the same description but normally all the + // main and meaningful pages are covered above or are nodes or terms. + $data['description'] = \Drupal::config('system.site')->get('slogan') ?? ''; + } } reliefweb_meta_update_metatags($metatags, $data); @@ -876,7 +844,7 @@ function reliefweb_meta_get_country_location_map_url($iso3) { function reliefweb_meta_page_attachments_alter(array &$attachments) { foreach ($attachments['#attached']['html_head'] ?? [] as $key => $attachment) { // Remove Drupal generator meta tag. - if ($attachment[1] == 'system_meta_generator') { + if (isset($attachment[1]) && $attachment[1] == 'system_meta_generator') { unset($attachments['#attached']['html_head'][$key]); } } diff --git a/html/modules/custom/reliefweb_rivers/js/advanced-search.js b/html/modules/custom/reliefweb_rivers/js/advanced-search.js index 503722d75..d217c3881 100644 --- a/html/modules/custom/reliefweb_rivers/js/advanced-search.js +++ b/html/modules/custom/reliefweb_rivers/js/advanced-search.js @@ -1771,7 +1771,9 @@ var parameter = advancedSearch.submitForm.querySelector('input[name="advanced-search"]'); if (!parameter) { parameter = createElement('input', {type: 'hidden', name: 'advanced-search'}); - advancedSearch.submitForm.insertBefore(parameter, advancedSearch.submitForm.firstChild); + // Try to preserve the order of the parameters. + var view = advancedSearch.submitForm.querySelector('input[name="view"]'); + advancedSearch.submitForm.insertBefore(parameter, view ? view.nextSibling : advancedSearch.submitForm.firstChild); } advancedSearch.parameter = parameter; diff --git a/html/modules/custom/reliefweb_rivers/src/AdvancedSearch.php b/html/modules/custom/reliefweb_rivers/src/AdvancedSearch.php index f1d9ee03a..4f57b0285 100644 --- a/html/modules/custom/reliefweb_rivers/src/AdvancedSearch.php +++ b/html/modules/custom/reliefweb_rivers/src/AdvancedSearch.php @@ -213,7 +213,7 @@ protected function computeAdvancedSearchData() { } // Generate a link to clear the filter selection when javascript is not - // availble. + // available. $remove = RiverServiceBase::getRiverUrl( $this->bundle, $this->parameters->getAll(['advanced-search']) @@ -1051,6 +1051,11 @@ public static function loadReferenceValues(array $filter, array $values = [], $s $query->condition('td.tid', $filter['exclude'], 'NOT IN'); $values = array_diff($values, $filter['exclude']); } + // Include only some terms. + elseif (!empty($filter['include'])) { + $query->condition('td.tid', $filter['include'], 'IN'); + $values = array_intersect($values, $filter['include']); + } // Filter by the given values if any. if (!empty($values)) { diff --git a/html/modules/custom/reliefweb_rivers/src/Controller/SearchConverter.php b/html/modules/custom/reliefweb_rivers/src/Controller/SearchConverter.php index 3c67d6cfd..7674e8390 100644 --- a/html/modules/custom/reliefweb_rivers/src/Controller/SearchConverter.php +++ b/html/modules/custom/reliefweb_rivers/src/Controller/SearchConverter.php @@ -11,8 +11,6 @@ use Drupal\Core\Session\AccountProxyInterface; use Drupal\Core\Url; use Drupal\reliefweb_api\Services\ReliefWebApiClient; -use Drupal\reliefweb_rivers\AdvancedSearch; -use Drupal\reliefweb_rivers\Parameters; use Drupal\reliefweb_rivers\RiverServiceBase; use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\HttpFoundation\JsonResponse; @@ -254,64 +252,13 @@ protected function parseSearchUrl($search_url) { return []; } - $river_data = RiverServiceBase::getRiverServiceFromUrl($search_url); - if (empty($river_data)) { + $service = RiverServiceBase::getRiverServiceFromUrl($search_url); + if (empty($service)) { return []; } - $service = $river_data['service']; - - // Parse the query from the search URL. - parse_str(parse_url($search_url, PHP_URL_QUERY) ?? '', $query); - $parameters = new Parameters($query); - if (isset($river['view'])) { - $parameters->set('view', $river['view']); - } - - // Retrieve the view used in the search URL to get the proper API payload. - $view = $parameters->get('view'); - $views = $service->getViews(); - $view = isset($views[$view]) ? $view : $service->getDefaultView(); - - // Get the base API payload for the view. - $payload = $service->getApiPayload($view); - - // Add the full text search query if any. - $search = trim($parameters->get('search', '')); - if (!empty($search)) { - $payload['query']['value'] = $search; - } - else { - unset($payload['query']); - } - - // Retrieve the API filter for the advanced search. - $advanced_search = new AdvancedSearch( - $service->getBundle(), - $service->getRiver(), - $parameters, - $service->getFilters(), - $service->getFilterSample() - ); - - // Merge the advanced search API filter with any other filter present - // in the API payload. - $filter = $advanced_search->getApiFilter(); - if (!empty($filter)) { - // Update the payload filter. - if (!empty($payload['filter'])) { - $payload['filter'] = [ - 'conditions' => [ - $payload['filter'], - $filter, - ], - 'operator' => 'AND', - ]; - } - else { - $payload['filter'] = $filter; - } - } + // Get the payload ready for an API request. + $payload = $service->prepareApiRequest(); // Remove the fields and sort option as we, instead, add the preset and // profile to the API URL. diff --git a/html/modules/custom/reliefweb_rivers/src/Parameters.php b/html/modules/custom/reliefweb_rivers/src/Parameters.php index 1c91fdd12..b88159fdd 100644 --- a/html/modules/custom/reliefweb_rivers/src/Parameters.php +++ b/html/modules/custom/reliefweb_rivers/src/Parameters.php @@ -2,6 +2,7 @@ namespace Drupal\reliefweb_rivers; +use Drupal\reliefweb_utility\Helpers\TextHelper; use Drupal\reliefweb_utility\Helpers\UrlHelper; /** @@ -466,6 +467,25 @@ public function __construct( $this->parseRegions(); } + /** + * Create a parameters object from the given URL. + * + * @param string $url + * URL. + * @param array $exclude + * Parameters to exclude from the returned parameters. + * + * @return \Drupal\reliefweb_rivers\Parameters + * Parameters object. + */ + public static function createFromUrl($url, array $exclude = ['q', 'page']) { + $query = []; + if (is_string($url)) { + parse_str(parse_url($url, PHP_URL_QUERY), $query); + } + return new static($query, $exclude); + } + /** * Parse the parameters from the given query or the current one. * @@ -508,7 +528,7 @@ public function getAll(array $exclude = []) { // Trim string parameters. if (is_string($value)) { - $value = trim($value); + $value = TextHelper::trimText($value); if ($value === '') { continue; } @@ -520,6 +540,39 @@ public function getAll(array $exclude = []) { return $parameters; } + /** + * Get all the parameters excluding the given ones, sorted. + * + * @param array $exclude + * Parameters to exclude. + * @param array $order + * Order of the parameters. + * @param bool $include_others + * If FALSE, parameters that are not in the order list will not be included. + * + * @return array + * Sorted parameters. + */ + public function getAllSorted(array $exclude = [], array $order = [], $include_others = TRUE) { + $order = $order ?: [ + 'list', + 'view', + 'group', + 'advanced-search', + 'search', + 'page', + ]; + $unsorted = $this->getAll($exclude); + $sorted = []; + foreach ($order as $key) { + if (isset($unsorted[$key])) { + $sorted[$key] = $unsorted[$key]; + unset($unsorted[$key]); + } + } + return $include_others ? $sorted + $unsorted : $sorted; + } + /** * Get the query parameters. * @@ -537,7 +590,7 @@ public function get($name = NULL, $default = NULL) { } elseif (isset($this->parameters[$name])) { $parameter = $this->parameters[$name]; - return is_string($parameter) ? trim($parameter) : $parameter; + return is_string($parameter) ? TextHelper::trimText($parameter) : $parameter; } return $default; } @@ -562,7 +615,7 @@ public function getString($name, $default = '', $trim = TRUE) { else { $parameter = $default; } - return $trim ? trim($parameter) : $parameter; + return $trim ? TextHelper::trimText($parameter) : $parameter; } /** @@ -572,8 +625,13 @@ public function getString($name, $default = '', $trim = TRUE) { * Parameter name. * @param mixed $value * Parameter value. + * @param bool $trim + * If TRUE and $value is a string, then it will be trimmed. */ - public function set($name, $value = '') { + public function set($name, $value = '', $trim = TRUE) { + if ($trim && is_string($value)) { + $value = TextHelper::trimText($value); + } $this->parameters[$name] = $value; } diff --git a/html/modules/custom/reliefweb_rivers/src/RiverServiceBase.php b/html/modules/custom/reliefweb_rivers/src/RiverServiceBase.php index 53d6d11ef..b28b38e4a 100644 --- a/html/modules/custom/reliefweb_rivers/src/RiverServiceBase.php +++ b/html/modules/custom/reliefweb_rivers/src/RiverServiceBase.php @@ -138,6 +138,13 @@ abstract class RiverServiceBase implements RiverServiceInterface { */ protected $limit = 20; + /** + * River URL used to construct the service. + * + * @var string|null + */ + protected $originalUrl = NULL; + /** * Constructor. * @@ -159,6 +166,8 @@ abstract class RiverServiceBase implements RiverServiceInterface { * The renderer service. * @param \Drupal\Core\StringTranslation\TranslationInterface $string_translation * The translation manager service. + * @param string|null $url + * Optional river URL with parameters from which to construct the service. */ public function __construct( ConfigFactoryInterface $config_factory, @@ -169,7 +178,8 @@ public function __construct( ReliefWebApiClient $api_client, RequestStack $request_stack, RendererInterface $renderer, - TranslationInterface $string_translation + TranslationInterface $string_translation, + $url = NULL ) { $this->configFactory = $config_factory; $this->currentUser = $current_user; @@ -181,12 +191,31 @@ public function __construct( $this->renderer = $renderer; $this->stringTranslation = $string_translation; $this->url = static::getRiverUrl($this->getBundle()); + $this->originalUrl = $url; + } + + /** + * {@inheritdoc} + */ + public function createNewInstanceFromUrl($url = NULL) { + return new static( + $this->configFactory, + $this->currentUser, + $this->languageManager, + $this->pagerManager, + $this->pagerParameters, + $this->apiClient, + $this->requestStack, + $this->renderer, + $this->stringTranslation, + $url + ); } /** * {@inheritdoc} */ - abstract public function getPageTitle(); + abstract public function getDefaultPageTitle(); /** * {@inheritdoc} @@ -208,6 +237,11 @@ abstract public function getApiPayload($view = ''); */ abstract public function parseApiData(array $api_data, $view = ''); + /** + * {@inheritdoc} + */ + abstract public function getDefaultRiverDescription(); + /** * {@inheritdoc} */ @@ -243,6 +277,151 @@ public function getUrl() { return $this->url; } + /** + * {@inheritdoc} + */ + public function getPageTitle() { + $title = $this->getDefaultPageTitle(); + $view = $this->getSelectedView(); + + if (!$this->useDefaultTitle()) { + $selection = $this->getAdvancedSearch()->getAdvancedSearchFilterSelection(); + + if (count($selection) == 2) { + $title = $this->t('@label1 - @label2 @title', [ + '@label1' => $selection[0]['label'], + '@label2' => $selection[1]['label'], + '@title' => $title, + ]); + } + elseif (!empty($selection)) { + $title = $this->t('@label1 @title', [ + '@label1' => $selection[0]['label'], + '@title' => $title, + ]); + } + } + + // Add the selected view to the default title if not the default one. + if ($view !== $this->getDefaultView()) { + $title = $this->t('@title (@view)', [ + '@title' => $title, + '@view' => $this->getViewLabel($view), + ]); + } + + return $title; + } + + /** + * {@inheritdoc} + */ + public function useDefaultTitle() { + $skip = TRUE; + + // Skip if there is a search query as there can be infinite variations and + // it may change completely the meaning of the selected filters. + if (empty($this->getSearch())) { + $selection = $this->getAdvancedSearch()->getAdvancedSearchFilterSelection(); + $view = $this->getSelectedView(); + $allowed_types = $this->getAllowedFilterTypesForTitle(); + $excluded_codes = $this->getExcludedFilterCodesForTitle(); + + // If there is a view, we only generate a title with the first selection. + // Otherwise we accept a second selected filter as part of the title. + // This is to reduce allowed combinations and to avoid weirdness like + // selected "fee-based" on the Training (Free courses) river. + $max_allowed_filters = $view !== $this->getDefaultView() ? 1 : 2; + + // Check if we should avoid computing a title and use the default one. + $skip = empty($selection) || + count($selection) > $max_allowed_filters || + // Ensure the selection doesn't start with an exlusion filter (without). + $selection[0]['operator'] !== 'with' || + !isset($allowed_types[$selection[0]['type']]) || + isset($excluded_codes[$selection[0]['code']]); + + if (isset($selection[1])) { + $skip = $skip || + // We only allow combinations of different filter types, not values. + $selection[1]['code'] === $selection[0]['code'] || + // We only allow simple "A and B" combinations. + $selection[1]['operator'] !== 'and-with' || + !isset($allowed_types[$selection[1]['type']]) || + isset($excluded_codes[$selection[1]['code']]); + } + } + return $skip; + } + + /** + * {@inheritdoc} + */ + public function getAllowedFilterTypesForTitle() { + // We allow term reference filters and fixed values (like "free") because + // they have labels that work relatively when combined and have relatively + // limited number of items limiting the number of combinations. + return ['reference' => TRUE, 'fixed' => TRUE]; + } + + /** + * {@inheritdoc} + */ + public function getExcludedFilterCodesForTitle() { + // We skip the organization type filter because the terms don't + // associate well with the river title and also, there is no direct + // link to the organization type from most pages (sources use a URL + // with a search parameter). + return ['OT' => TRUE]; + } + + /** + * {@inheritdoc} + */ + public function getCanonicalUrl() { + $title = ''; + $parameters = []; + $advanced_search_parameter = $this->getAdvancedSearch()->getParameter(); + + // By adding the view to the canonical URL, we can make those variants + // more easily discoverable. + $view = $this->getSelectedView(); + if ($view !== $this->getDefaultView()) { + $title = $this->getPageTitle(); + $parameters['view'] = $view; + } + + // For some combinations of filters (ex: Afghanistan and Situation Report), + // we use a custom title and canonical URL so they can be indexed by search + // engines as their own pages. + // For other sets of fitlers, we ignore them and use the canonical URL of + // the page without filters. + // @see https://developers.google.com/search/blog/2014/02/faceted-navigation-best-and-5-of-worst + if (!$this->useDefaultTitle()) { + $title = $this->getPageTitle(); + $parameters['advanced-search'] = $advanced_search_parameter; + $add_page = TRUE; + } + else { + $add_page = empty($this->getSearch()) && empty($advanced_search_parameter); + } + + // Apparently, it is recommended to keep the `page` parameter for a + // paginated sequence so that they have their own canonical URL instead + // of pointing at the first page of the listing. + // @see https://developers.google.com/search/docs/specialty/ecommerce/pagination-and-incremental-page-loading#use-urls-correctly + if ($add_page) { + $page = $this->pagerManager->getPager()?->getCurrentPage(); + if (!empty($page)) { + $parameters['page'] = $page; + } + } + + // @todo We may want to add those URLs to the sitemap. + // @see https://developers.google.com/search/docs/crawling-indexing/consolidate-duplicate-urls#sitemap-method + return static::getRiverUrl($this->getBundle(), $parameters, $title, FALSE, TRUE); + } + /** * {@inheritdoc} */ @@ -267,7 +446,12 @@ public function getPageContent() { */ public function getParameters() { if (!isset($this->parameters)) { - $this->parameters = new Parameters(); + if (!empty($this->originalUrl)) { + $this->parameters = Parameters::createFromUrl($this->originalUrl); + } + else { + $this->parameters = new Parameters(); + } } return $this->parameters; } @@ -300,8 +484,35 @@ public function getDefaultView() { */ public function getSelectedView() { $view = $this->getParameters()->getString('view'); + return $this->validateView($view) ?? $this->getDefaultView(); + } + + /** + * {@inheritdoc} + */ + public function setSelectedView($view) { + $view = $this->validateView($view) ?? $this->getSelectedView(); + if ($view === $this->getDefaultView()) { + $this->getParameters()->remove('view'); + } + else { + $this->getParameters()->set('view', $view); + } + } + + /** + * {@inheritdoc} + */ + public function validateView($view) { $views = $this->getViews(); - return isset($views[$view]) ? $view : $this->getDefaultView(); + return isset($views[$view]) ? $view : NULL; + } + + /** + * {@inheritdoc} + */ + public function getViewLabel($view) { + return $this->getViews()[$view] ?? ''; } /** @@ -311,6 +522,13 @@ public function getSearch() { return $this->getParameters()->getString('search'); } + /** + * {@inheritdoc} + */ + public function setSearch($search) { + $this->getParameters()->set('search', $search, TRUE); + } + /** * {@inheritdoc} */ @@ -337,7 +555,7 @@ public function getRiverViews() { else { $item['url'] = static::getRiverUrl($this->getBundle(), [ 'view' => $id, - ]); + ], $title); } // Mark the current view as selected. @@ -512,7 +730,7 @@ public function getRiverLinks() { public function getRssLink() { try { return Url::fromRoute('reliefweb_rivers.' . $this->getBundle() . '.rss', [], [ - 'query' => $this->getParameters()->get(), + 'query' => $this->getParameters()->getAllSorted(['list']), 'absolute' => TRUE, ])->toString(); } @@ -525,8 +743,8 @@ public function getRssLink() { * {@inheritdoc} */ public function getApiLink() { - $parameters = $this->getParameters()->get(); - $search_url = static::getRiverUrl($this->getBundle(), $parameters, TRUE); + $parameters = $this->getParameters()->getAllSorted(['list']); + $search_url = static::getRiverUrl($this->getBundle(), $parameters, '', FALSE, TRUE); $options = [ 'query' => [ 'appname' => 'rwint-user-' . $this->currentUser->id(), @@ -557,9 +775,9 @@ public function getFilterSample() { /** * {@inheritdoc} */ - public function getApiData($limit = 20) { - $view = $this->getSelectedView(); - $page = $this->pagerParameters->findPage(); + public function prepareApiRequest($limit = 20, $paginated = TRUE, $view = NULL) { + $view = $this->validateView($view) ?? $this->getSelectedView(); + $page = $paginated ? $this->pagerParameters->findPage() : 0; $offset = $page * $limit; $payload = $this->getApiPayload($view); @@ -593,9 +811,18 @@ public function getApiData($limit = 20) { $payload['filter'] = $filter; } } + return $payload; + } + + /** + * {@inheritdoc} + */ + public function getApiData($limit = 20, $paginated = TRUE, array $payload = NULL, $view = NULL) { + $view = $this->validateView($view) ?? $this->getSelectedView(); + $payload = $payload ?? $this->prepareApiRequest($limit, $paginated, $view); // Retrieve the API data. - $data = $this->requestApi($payload); + $data = $this->requestApi($payload, $paginated); // Skip if there is no data. if (empty($data)) { @@ -603,7 +830,9 @@ public function getApiData($limit = 20) { } // Initialize the pager. - $this->pagerManager->createPager($data['totalCount'] ?? 0, $limit); + if ($paginated) { + $this->pagerManager->createPager($data['totalCount'] ?? 0, $limit); + } // Parse the API data and return the entities. return $this->parseApiData($data, $view); @@ -728,7 +957,7 @@ public function parseApiDataForRss(array $data, $view = '') { * {@inheritdoc} */ public function getRiverDescription() { - $title = $this->getPageTitle(); + $title = $this->getDefaultPageTitle(); $search = $this->getSearch(); $filters = $this->getAdvancedSearch()->getHumanReadableSelection(); @@ -736,34 +965,37 @@ public function getRiverDescription() { if ($view !== $this->getDefaultView()) { $title = $this->t('@title (@view)', [ '@title' => $title, - '@view' => $this->getViews()[$view], + '@view' => $this->getViewLabel($view), ]); } if (!empty($search) && !empty($filters)) { - return $this->t('@title containing @search and @filters', [ + $description = $this->t('@title containing @search and @filters', [ '@title' => $title, '@search' => $search, '@filters' => $filters, ]); } elseif (!empty($search)) { - return $this->t('@title containing @search', [ + $description = $this->t('@title containing @search', [ '@title' => $title, '@search' => $search, ]); } elseif (!empty($filters)) { - return $this->t('@title @filters', [ + $description = $this->t('@title @filters', [ '@title' => $title, '@filters' => $filters, ]); } elseif ($view !== $this->getDefaultView()) { - return $title; + $description = $title; + } + else { + $description = $this->getDefaultRiverDescription(); } - return NULL; + return $description; } /** @@ -802,16 +1034,57 @@ public static function createDate($date) { /** * {@inheritdoc} */ - public static function getRiverUrl($bundle, array $parameters = [], $absolute = FALSE) { + public static function getRiverUrl($bundle, array $parameters = [], $title = '', $partial_title = FALSE, $absolute = FALSE) { try { - return Url::fromRoute('reliefweb_rivers.' . $bundle . '.river', [], [ - 'query' => $parameters, - 'absolute' => $absolute, - ])->toString(); + $url = Url::fromRoute('reliefweb_rivers.' . $bundle . '.river', []); } catch (RouteNotFoundException $exception) { return ''; } + + $title = !empty($partial_title) ? static::getRiverUrlTitle($bundle, $title) : $title; + if (!empty($title)) { + $parameters = ['list' => $title] + $parameters; + } + + return $url + ->setOption('query', $parameters) + ->setOption('absolute', $absolute) + ->toString(); + } + + /** + * {@inheritdoc} + */ + public static function getRiverUrlTitle($bundle, $prefix) { + if (!empty($prefix)) { + switch ($bundle) { + case 'blog_post': + return t('@prefix Blog Posts', ['@prefix' => $prefix]); + + case 'country': + return t('@prefix Countries', ['@prefix' => $prefix]); + + case 'disaster': + return t('@prefix Disasters', ['@prefix' => $prefix]); + + case 'job': + return t('@prefix Jobs', ['@prefix' => $prefix]); + + case 'source': + return t('@prefix Organizations', ['@prefix' => $prefix]); + + case 'topic': + return t('@prefix Topics', ['@prefix' => $prefix]); + + case 'training': + return t('@prefix Training Opportunities', ['@prefix' => $prefix]); + + case 'report': + return t('@prefix Updates', ['@prefix' => $prefix]); + } + } + return ''; } /** @@ -929,17 +1202,28 @@ public static function getRiverMapping() { * entity bundle and view. */ public static function getRiverServiceFromUrl($url) { + static $instances = []; if (is_string($url)) { - $mapping = static::getRiverMapping(); - $path = trim(parse_url($url, PHP_URL_PATH), '/'); - - if (isset($mapping[$path]['bundle'])) { - $data = $mapping[$path]; - $service = static::getRiverService($data['bundle']); - if (isset($service)) { - return $data + ['service' => $service]; + if (!isset($instances[$url])) { + $mapping = static::getRiverMapping(); + $path = trim(parse_url($url, PHP_URL_PATH), '/'); + + if (isset($mapping[$path]['bundle'])) { + $data = $mapping[$path]; + $service = static::getRiverService($data['bundle']); + + if (isset($service)) { + // Create a new copy of the service with the given URL. + $service = $service->createNewInstanceFromUrl($url); + // Override the view. + if (isset($data['view'])) { + $service->setSelectedView($data['view']); + } + $instances[$url] = $service; + } } } + return $instances[$url] ?? NULL; } return []; } diff --git a/html/modules/custom/reliefweb_rivers/src/RiverServiceInterface.php b/html/modules/custom/reliefweb_rivers/src/RiverServiceInterface.php index 2bd59c193..1c89fa7f5 100644 --- a/html/modules/custom/reliefweb_rivers/src/RiverServiceInterface.php +++ b/html/modules/custom/reliefweb_rivers/src/RiverServiceInterface.php @@ -39,6 +39,14 @@ public function getEntityTypeId(); */ public function getResource(); + /** + * Get the default page title for the river. + * + * @return string + * Page title. + */ + public function getDefaultPageTitle(); + /** * Get the page title for the river. * @@ -47,6 +55,31 @@ public function getResource(); */ public function getPageTitle(); + /** + * Check it we should use the default title or customize it. + * + * @return bool + * TRUE if we should use the default tile (for example if there is a search + * query). + */ + public function useDefaultTitle(); + + /** + * Get allowed filter types for custom titles and canonical URLs. + * + * @return array + * List of allowed filter types. + */ + public function getAllowedFilterTypesForTitle(); + + /** + * Get excluded filter codes for custom titles and canonical URLs. + * + * @return array + * List of excluded filter codes + */ + public function getExcludedFilterCodesForTitle(); + /** * Get the page content for the river. * @@ -63,6 +96,14 @@ public function getPageContent(); */ public function getUrl(); + /** + * Get the canonical URL for the river. + * + * @return string + * Page title. + */ + public function getCanonicalUrl(); + /** * Get the river parameter handler. * @@ -103,6 +144,37 @@ public function getDefaultView(); */ public function getSelectedView(); + /** + * Set the selected view for the river. + * + * @param string $view + * The view to select. If it's invalid, the current selected view or the + * default view will be used. + */ + public function setSelectedView($view); + + /** + * Validate a view against the allowed views for the river. + * + * @param string $view + * View. + * + * @return string|null + * The view if valid or NULL othewise. + */ + public function validateView($view); + + /** + * Get the label for a view. + * + * @param string $view + * View. + * + * @return string + * View label or empty string if the view doesn't exist. + */ + public function getViewLabel($view); + /** * Get the sanitized search parameter. * @@ -111,6 +183,14 @@ public function getSelectedView(); */ public function getSearch(); + /** + * Set the sanitized search parameter. + * + * @param string $search + * Sanitized search parmeter. + */ + public function setSearch($search); + /** * Get the list of filters for the advaneced search. * @@ -208,23 +288,47 @@ public function getRssLink(); public function getApiLink(); /** - * Get the ReliefWeb API payload for the given river and view. + * Get the base ReliefWeb API payload for the given river and view. * * @return array * API payload. */ public function getApiPayload($view = ''); + /** + * Prepare the API payload with the river parameters. + * + * @param int $limit + * Number of resources to return. + * @param bool $paginated + * If TRUE, add an offset to the payload based on the current page + * parameter. + * @param string|null $view + * Optional view override. + * + * @return array + * Payload ready for an API request. + */ + public function prepareApiRequest($limit = 20, $paginated = TRUE, $view = NULL); + /** * Get the data from the ReliefWeb API. * * @param int $limit * Number of resources to return. + * @param bool $paginated + * If TRUE, add an offset to the payload based on the current page + * parameter. + * @param array|null $payload + * Optional payload override. If NULL, the payload generated by + * ::prepareApiRequest() will be used for the query to the API. + * @param string|null $view + * Optional view override. * * @return array * List of resource data as returned by ::parseApiData(). */ - public function getApiData($limit = 20); + public function getApiData($limit = 20, $paginated = TRUE, array $payload = NULL, $view = NULL); /** * Parse the data from the ReliefWeb API to use in rivers. @@ -296,11 +400,20 @@ public function parseApiDataForRss(array $data, $view = ''); /** * Get the river description based on the filters and search query. * - * @return \Drupal\Component\Render\MarkupInterface|null - * River page description or empty if there is no search or filters. + * @return \Drupal\Component\Render\MarkupInterface + * River page description based on the search and filters or the default + * river page description otherwise. */ public function getRiverDescription(); + /** + * Get the default river description. + * + * @return \Drupal\Component\Render\MarkupInterface + * The default river page description. + */ + public function getDefaultRiverDescription(); + /** * Get the ISO 639-1 language code for the entity. * @@ -332,13 +445,18 @@ public static function createDate($date); * Entity bundle associated with the river. * @param array $parameters * Query parameters. + * @param string $title + * Title to use as parameter in the river URL. + * @param bool $partial_title + * If TRUE, then the name of the river will be appended to the given $title + * if not empty. * @param bool $absolute * If TRUE, return an absolute URL. * * @return string * River URL. */ - public static function getRiverUrl($bundle, array $parameters = [], $absolute = FALSE); + public static function getRiverUrl($bundle, array $parameters = [], $title = '', $partial_title = FALSE, $absolute = FALSE); /** * Get the data of the river for given bundle and API data. @@ -383,6 +501,17 @@ public static function getRiverApiPayload($bundle, $view = '', array $exclude = */ public static function getRiverService($bundle); + /** + * Get the river service from a river URL. + * + * @param string $url + * River url. + * + * @return Drupal\reliefweb_rivers\RiverServiceInterface|null + * The river service or NULL if not found. + */ + public static function getRiverServiceFromUrl($url); + /** * Get the cache tags for the river. * diff --git a/html/modules/custom/reliefweb_rivers/src/Services/BlogPostRiver.php b/html/modules/custom/reliefweb_rivers/src/Services/BlogPostRiver.php index 31fae20b7..c142aaba8 100644 --- a/html/modules/custom/reliefweb_rivers/src/Services/BlogPostRiver.php +++ b/html/modules/custom/reliefweb_rivers/src/Services/BlogPostRiver.php @@ -36,6 +36,13 @@ class BlogPostRiver extends RiverServiceBase { * {@inheritdoc} */ public function getPageTitle() { + return $this->getDefaultPageTitle(); + } + + /** + * {@inheritdoc} + */ + public function getDefaultPageTitle() { return $this->t('Blog'); } @@ -248,6 +255,13 @@ public function parseApiDataForRss(array $data, $view = '') { return $entities; } + /** + * {@inheritdoc} + */ + public function getDefaultRiverDescription() { + return $this->t("A look at the ideas and projects we're working on as we strive to grow and improve ReliefWeb."); + } + /** * {@inheritdoc} */ diff --git a/html/modules/custom/reliefweb_rivers/src/Services/CountryRiver.php b/html/modules/custom/reliefweb_rivers/src/Services/CountryRiver.php index ce8fad620..17d4cb30a 100644 --- a/html/modules/custom/reliefweb_rivers/src/Services/CountryRiver.php +++ b/html/modules/custom/reliefweb_rivers/src/Services/CountryRiver.php @@ -39,7 +39,7 @@ class CountryRiver extends RiverServiceBase { /** * {@inheritdoc} */ - public function getPageTitle() { + public function getDefaultPageTitle() { return $this->t('Countries'); } @@ -198,6 +198,13 @@ public function parseApiData(array $api_data, $view = '') { return $entities; } + /** + * {@inheritdoc} + */ + public function getDefaultRiverDescription() { + return $this->t('ReliefWeb country pages provide a comprehensive overview of the humanitarian situation and feature situation reports, news and press releases, assessments, evaluations, infographics, maps and more.'); + } + /** * {@inheritdoc} */ diff --git a/html/modules/custom/reliefweb_rivers/src/Services/DisasterRiver.php b/html/modules/custom/reliefweb_rivers/src/Services/DisasterRiver.php index 13191c637..44053036b 100644 --- a/html/modules/custom/reliefweb_rivers/src/Services/DisasterRiver.php +++ b/html/modules/custom/reliefweb_rivers/src/Services/DisasterRiver.php @@ -38,7 +38,7 @@ class DisasterRiver extends RiverServiceBase { /** * {@inheritdoc} */ - public function getPageTitle() { + public function getDefaultPageTitle() { return $this->t('Disasters'); } @@ -225,7 +225,7 @@ public function parseApiData(array $api_data, $view = '') { 'name' => $type['name'], 'url' => static::getRiverUrl($this->bundle, [ 'advanced-search' => '(TY' . $type['id'] . ')', - ]), + ], $type['name'], TRUE), 'main' => !empty($country['primary']), ]; } @@ -368,6 +368,13 @@ public function requestApi(array $payload) { return parent::requestApi($payload); } + /** + * {@inheritdoc} + */ + public function getDefaultRiverDescription() { + return $this->t('ReliefWeb disaster pages provide an overview of the situation and situation reports, news and press releases, assessments, evaluations, infographics and maps. Browse our list of natural disasters with humanitarian impact from 1981 until today.'); + } + /** * {@inheritdoc} */ diff --git a/html/modules/custom/reliefweb_rivers/src/Services/JobRiver.php b/html/modules/custom/reliefweb_rivers/src/Services/JobRiver.php index 9582b6fb0..4de0d88e9 100644 --- a/html/modules/custom/reliefweb_rivers/src/Services/JobRiver.php +++ b/html/modules/custom/reliefweb_rivers/src/Services/JobRiver.php @@ -35,7 +35,7 @@ class JobRiver extends RiverServiceBase { /** * {@inheritdoc} */ - public function getPageTitle() { + public function getDefaultPageTitle() { return $this->t('Jobs'); } @@ -54,7 +54,7 @@ public function getViews() { * {@inheritdoc} */ public function getFilters() { - return [ + $filters = [ 'TY' => [ 'name' => $this->t('Job type'), 'type' => 'reference', @@ -167,6 +167,13 @@ public function getFilters() { ], ], ]; + // It doesn't make sense to display the country filter when the view is + // for jobs without a location. + $view = $this->getSelectedView(); + if ($view === 'unspecified-location') { + unset($filters['C']); + } + return $filters; } /** @@ -276,7 +283,7 @@ public function parseApiData(array $api_data, $view = '') { 'code' => $country['iso3'] ?? '', 'url' => static::getRiverUrl($this->bundle, [ 'advanced-search' => '(C' . $country['id'] . ')', - ]), + ], $country['name'], TRUE), 'main' => !empty($country['primary']), ]; } @@ -290,7 +297,7 @@ public function parseApiData(array $api_data, $view = '') { 'shortname' => $source['shortname'] ?? $source['name'], 'url' => static::getRiverUrl($this->bundle, [ 'advanced-search' => '(S' . $source['id'] . ')', - ]), + ], $source['name'], TRUE), ]; } $tags['source'] = $sources; @@ -437,4 +444,11 @@ public function parseApiDataForRss(array $data, $view = '') { return $entities; } + /** + * {@inheritdoc} + */ + public function getDefaultRiverDescription() { + return $this->t('Your gateway for humanitarian and development jobs. Search and/or drill down with filters to narrow down the listings.'); + } + } diff --git a/html/modules/custom/reliefweb_rivers/src/Services/ReportRiver.php b/html/modules/custom/reliefweb_rivers/src/Services/ReportRiver.php index 04e89635d..018b4f06a 100644 --- a/html/modules/custom/reliefweb_rivers/src/Services/ReportRiver.php +++ b/html/modules/custom/reliefweb_rivers/src/Services/ReportRiver.php @@ -35,10 +35,26 @@ class ReportRiver extends RiverServiceBase { /** * {@inheritdoc} */ - public function getPageTitle() { + public function getDefaultPageTitle() { return $this->t('Updates'); } + /** + * {@inheritdoc} + */ + public function getExcludedFilterCodesForTitle() { + // We don't want to use the country (C) field for the updates river, because + // that leads to duplicate or weird titles with the primary country field, + // ex: "Afghanistan - Aghanistan Updates"... + // See RiverServiceBase for the explanation about the exclusion of 'OT'. + $codes = ['C' => TRUE, 'OT' => TRUE]; + // It doesn't make sense to have Maps + Situation Report for example. + if ($this->getSelectedView() === 'maps') { + $codes['F'] = TRUE; + } + return $codes; + } + /** * {@inheritdoc} */ @@ -55,7 +71,7 @@ public function getViews() { * {@inheritdoc} */ public function getFilters() { - return [ + $filters = [ 'PC' => [ 'name' => $this->t('Primary country'), 'shortname' => TRUE, @@ -198,6 +214,20 @@ public function getFilters() { ], ], ]; + + // Only include map, infographic and interactive content formats when + // viewing maps/infographics. + $view = $this->getSelectedView(); + if ($view === 'maps') { + $filters['F']['include'] = [12, 12570, 38974]; + } + // Exclude map, infographic and interactive content formats when viewing + // reports only. + elseif ($view === 'reports') { + $filters['F']['exclude'] = [12, 12570, 38974]; + } + + return $filters; } /** @@ -662,4 +692,11 @@ public function parseApiDataForRss(array $data, $view = '') { return $entities; } + /** + * {@inheritdoc} + */ + public function getDefaultRiverDescription() { + return $this->t('Your gateway to all content to date. Search and/or drill down with filters to narrow down the content.'); + } + } diff --git a/html/modules/custom/reliefweb_rivers/src/Services/SourceRiver.php b/html/modules/custom/reliefweb_rivers/src/Services/SourceRiver.php index e816662aa..b4f420cde 100644 --- a/html/modules/custom/reliefweb_rivers/src/Services/SourceRiver.php +++ b/html/modules/custom/reliefweb_rivers/src/Services/SourceRiver.php @@ -42,10 +42,17 @@ class SourceRiver extends RiverServiceBase { /** * {@inheritdoc} */ - public function getPageTitle() { + public function getDefaultPageTitle() { return $this->t('Organizations'); } + /** + * {@inheritdoc} + */ + public function getPageTitle() { + return $this->getDefaultPageTitle(); + } + /** * {@inheritdoc} */ @@ -149,7 +156,8 @@ public function parseApiData(array $api_data, $view = '') { // Get the source ids. $ids = []; foreach ($items as $item) { - $ids[] = $item['id']; + // @todo Append the shortname? + $ids[$item['id']] = $item['fields']['name'] ?? ''; } // Get the publications of the sources. @@ -207,7 +215,7 @@ public function parseApiData(array $api_data, $view = '') { * @param string $field * Field name (without the 'field_' prefix) used to tag the taxonomy terms. * @param array $ids - * List of taxonomy term ids. + * List of taxonomy term ids (id as key, name as value). * * @return array * Associative array with the term ids as keys and the total of published @@ -218,8 +226,6 @@ public function getPublications($field, array $ids) { return []; } - $sources = array_combine($ids, $ids); - // API request payload (already encoded as it's the same for all the // queries). $payload = json_encode([ @@ -267,8 +273,9 @@ public function getPublications($field, array $ids) { $resource = $queries[$index]['resource']; if (isset($data['embedded']['facets']['source']['data'])) { foreach ($data['embedded']['facets']['source']['data'] as $item) { - if (isset($sources[$item['value']]) && !empty($item['count'])) { + if (isset($ids[$item['value']]) && !empty($item['count'])) { $id = $item['value']; + $name = $ids[$id]; $count = (int) $item['count']; switch ($resource) { @@ -279,7 +286,7 @@ public function getPublications($field, array $ids) { ]), 'url' => static::getRiverUrl('report', [ 'advanced-search' => '(S' . $id . ')', - ]), + ], $name, TRUE), ]; break; @@ -290,7 +297,7 @@ public function getPublications($field, array $ids) { ]), 'url' => static::getRiverUrl('job', [ 'advanced-search' => '(S' . $id . ')', - ]), + ], $name, TRUE), ]; break; @@ -301,7 +308,7 @@ public function getPublications($field, array $ids) { ]), 'url' => static::getRiverUrl('training', [ 'advanced-search' => '(S' . $id . ')', - ]), + ], $name, TRUE), ]; break; } @@ -380,6 +387,13 @@ public static function getFirstLetters() { return $letters; } + /** + * {@inheritdoc} + */ + public function getDefaultRiverDescription() { + return $this->t('A list of organizations that are actively providing ReliefWeb with content (reports, jobs and training).'); + } + /** * {@inheritdoc} */ diff --git a/html/modules/custom/reliefweb_rivers/src/Services/TopicRiver.php b/html/modules/custom/reliefweb_rivers/src/Services/TopicRiver.php index c529e5262..2ac669766 100644 --- a/html/modules/custom/reliefweb_rivers/src/Services/TopicRiver.php +++ b/html/modules/custom/reliefweb_rivers/src/Services/TopicRiver.php @@ -51,10 +51,17 @@ class TopicRiver extends RiverServiceBase { /** * {@inheritdoc} */ - public function getPageTitle() { + public function getDefaultPageTitle() { return $this->t('Topics'); } + /** + * {@inheritdoc} + */ + public function getPageTitle() { + return $this->getDefaultPageTitle(); + } + /** * {@inheritdoc} */ @@ -239,4 +246,11 @@ public function parseApiData(array $api_data, $view = '') { // Not used. } + /** + * {@inheritdoc} + */ + public function getDefaultRiverDescription() { + return $this->t('Curated pages dedicated to humanitarian themes and specific humanitarian crises.'); + } + } diff --git a/html/modules/custom/reliefweb_rivers/src/Services/TrainingRiver.php b/html/modules/custom/reliefweb_rivers/src/Services/TrainingRiver.php index 6f06d63e2..642012141 100644 --- a/html/modules/custom/reliefweb_rivers/src/Services/TrainingRiver.php +++ b/html/modules/custom/reliefweb_rivers/src/Services/TrainingRiver.php @@ -35,8 +35,8 @@ class TrainingRiver extends RiverServiceBase { /** * {@inheritdoc} */ - public function getPageTitle() { - return $this->t('Training'); + public function getDefaultPageTitle() { + return $this->t('Training Opportunities'); } /** @@ -56,7 +56,7 @@ public function getViews() { * {@inheritdoc} */ public function getFilters() { - return [ + $filters = [ 'TY' => [ 'name' => $this->t('Category'), 'type' => 'reference', @@ -206,6 +206,20 @@ public function getFilters() { ], ], ]; + // It doesn't make sense to display the cost filter when the view is + // for free training. + $view = $this->getSelectedView(); + if ($view === 'free') { + unset($filters['CO']); + } + // It doesn't make sense to display the date filters when the view is + // for ongoing training. + elseif ($view === 'ongoing') { + unset($filters['DS']); + unset($filters['DE']); + unset($filters['DR']); + } + return $filters; } /** @@ -348,7 +362,7 @@ public function parseApiData(array $api_data, $view = '') { 'code' => $country['iso3'] ?? '', 'url' => static::getRiverUrl($this->bundle, [ 'advanced-search' => '(C' . $country['id'] . ')', - ]), + ], $country['name'], TRUE), 'main' => !empty($country['primary']), ]; } @@ -362,7 +376,7 @@ public function parseApiData(array $api_data, $view = '') { 'shortname' => $source['shortname'] ?? $source['name'], 'url' => static::getRiverUrl($this->bundle, [ 'advanced-search' => '(S' . $source['id'] . ')', - ]), + ], $source['name'], TRUE), ]; } $tags['source'] = $sources; @@ -531,4 +545,11 @@ public function parseApiDataForRss(array $data, $view = '') { return $entities; } + /** + * {@inheritdoc} + */ + public function getDefaultRiverDescription() { + return $this->t('Your gateway for humanitarian training opportunities. Search and/or drill down with filters to narrow down the listings.'); + } + } From 1373553c2993b986f8ab535e39a7ac7ba65fb96f Mon Sep 17 00:00:00 2001 From: orakili Date: Tue, 14 Feb 2023 03:38:26 +0000 Subject: [PATCH 2/6] fix: metatags for rivers and comment Refs: RW-700 --- html/modules/custom/reliefweb_meta/reliefweb_meta.module | 2 +- .../custom/reliefweb_rivers/src/RiverServiceBase.php | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/html/modules/custom/reliefweb_meta/reliefweb_meta.module b/html/modules/custom/reliefweb_meta/reliefweb_meta.module index 1092a60a1..b3aa262e4 100644 --- a/html/modules/custom/reliefweb_meta/reliefweb_meta.module +++ b/html/modules/custom/reliefweb_meta/reliefweb_meta.module @@ -53,7 +53,7 @@ function reliefweb_meta_metatags_alter(array &$metatags, array &$context) { ]; // River pages. - if (strpos($route_name, 'reliefweb_rivers.') === 0) { + if (preg_match('/^reliefweb_rivers\.[^.]+\.river$/', $route_name) === 1) { try { // The river services have the same name as the corresponding route name. $river_service = \Drupal::service($route_name); diff --git a/html/modules/custom/reliefweb_rivers/src/RiverServiceBase.php b/html/modules/custom/reliefweb_rivers/src/RiverServiceBase.php index b28b38e4a..c27fca91a 100644 --- a/html/modules/custom/reliefweb_rivers/src/RiverServiceBase.php +++ b/html/modules/custom/reliefweb_rivers/src/RiverServiceBase.php @@ -1197,9 +1197,9 @@ public static function getRiverMapping() { * @param string $url * River url. * - * @return array - * If there is a match, then an array with the river service, associated - * entity bundle and view. + * @return \Drupal\reliefweb_rivers\RiverServiceInferface|null + * If there is a match, then the service for the corresponding river, + * NULL otherwise. */ public static function getRiverServiceFromUrl($url) { static $instances = []; From 3e8dfb44abfbc8a38f1145e99cfb30a4b48fb561 Mon Sep 17 00:00:00 2001 From: orakili Date: Tue, 14 Feb 2023 05:57:18 +0000 Subject: [PATCH 3/6] fix: river service signature Refs: RW-700 --- .../reliefweb_rivers/src/RiverServiceBase.php | 38 ++++++++----------- .../src/RiverServiceInterface.php | 8 ++++ .../src/Services/TopicRiver.php | 21 ++++++++++ 3 files changed, 44 insertions(+), 23 deletions(-) diff --git a/html/modules/custom/reliefweb_rivers/src/RiverServiceBase.php b/html/modules/custom/reliefweb_rivers/src/RiverServiceBase.php index c27fca91a..e29006f75 100644 --- a/html/modules/custom/reliefweb_rivers/src/RiverServiceBase.php +++ b/html/modules/custom/reliefweb_rivers/src/RiverServiceBase.php @@ -138,13 +138,6 @@ abstract class RiverServiceBase implements RiverServiceInterface { */ protected $limit = 20; - /** - * River URL used to construct the service. - * - * @var string|null - */ - protected $originalUrl = NULL; - /** * Constructor. * @@ -166,8 +159,6 @@ abstract class RiverServiceBase implements RiverServiceInterface { * The renderer service. * @param \Drupal\Core\StringTranslation\TranslationInterface $string_translation * The translation manager service. - * @param string|null $url - * Optional river URL with parameters from which to construct the service. */ public function __construct( ConfigFactoryInterface $config_factory, @@ -178,8 +169,7 @@ public function __construct( ReliefWebApiClient $api_client, RequestStack $request_stack, RendererInterface $renderer, - TranslationInterface $string_translation, - $url = NULL + TranslationInterface $string_translation ) { $this->configFactory = $config_factory; $this->currentUser = $current_user; @@ -191,14 +181,13 @@ public function __construct( $this->renderer = $renderer; $this->stringTranslation = $string_translation; $this->url = static::getRiverUrl($this->getBundle()); - $this->originalUrl = $url; } /** * {@inheritdoc} */ public function createNewInstanceFromUrl($url = NULL) { - return new static( + $service = new static( $this->configFactory, $this->currentUser, $this->languageManager, @@ -207,9 +196,10 @@ public function createNewInstanceFromUrl($url = NULL) { $this->apiClient, $this->requestStack, $this->renderer, - $this->stringTranslation, - $url + $this->stringTranslation ); + $service->setParameters(Parameters::createFromUrl($url)); + return $service; } /** @@ -446,16 +436,18 @@ public function getPageContent() { */ public function getParameters() { if (!isset($this->parameters)) { - if (!empty($this->originalUrl)) { - $this->parameters = Parameters::createFromUrl($this->originalUrl); - } - else { - $this->parameters = new Parameters(); - } + $this->parameters = new Parameters(); } return $this->parameters; } + /** + * {@inheritdoc} + */ + public function setParameters(Parameters $parameters) { + return $this->parameters = $parameters; + } + /** * {@inheritdoc} */ @@ -822,7 +814,7 @@ public function getApiData($limit = 20, $paginated = TRUE, array $payload = NULL $payload = $payload ?? $this->prepareApiRequest($limit, $paginated, $view); // Retrieve the API data. - $data = $this->requestApi($payload, $paginated); + $data = $this->requestApi($payload); // Skip if there is no data. if (empty($data)) { @@ -842,7 +834,7 @@ public function getApiData($limit = 20, $paginated = TRUE, array $payload = NULL * {@inheritdoc} */ public function requestApi(array $payload) { - return $this->apiClient->request($this->resource, $payload); + return $this->apiClient->request($this->getResource(), $payload); } /** diff --git a/html/modules/custom/reliefweb_rivers/src/RiverServiceInterface.php b/html/modules/custom/reliefweb_rivers/src/RiverServiceInterface.php index 1c89fa7f5..4e68b960a 100644 --- a/html/modules/custom/reliefweb_rivers/src/RiverServiceInterface.php +++ b/html/modules/custom/reliefweb_rivers/src/RiverServiceInterface.php @@ -112,6 +112,14 @@ public function getCanonicalUrl(); */ public function getParameters(); + /** + * Set the river parameter handler. + * + * @param \Drupal\reliefweb_rivers\Parameters $parameters + * River parameter handler. + */ + public function setParameters(Parameters $parameters); + /** * Get the advanced search handler. * diff --git a/html/modules/custom/reliefweb_rivers/src/Services/TopicRiver.php b/html/modules/custom/reliefweb_rivers/src/Services/TopicRiver.php index 2ac669766..07da69df8 100644 --- a/html/modules/custom/reliefweb_rivers/src/Services/TopicRiver.php +++ b/html/modules/custom/reliefweb_rivers/src/Services/TopicRiver.php @@ -12,6 +12,7 @@ use Drupal\Core\Session\AccountProxyInterface; use Drupal\Core\StringTranslation\TranslationInterface; use Drupal\reliefweb_api\Services\ReliefWebApiClient; +use Drupal\reliefweb_rivers\Parameters; use Drupal\reliefweb_rivers\RiverServiceBase; use Drupal\reliefweb_utility\Helpers\HtmlSummarizer; use Symfony\Component\HttpFoundation\RequestStack; @@ -135,6 +136,26 @@ public function __construct( $this->entityTypeManager = $entity_type_manager; } + /** + * {@inheritdoc} + */ + public function createNewInstanceFromUrl($url = NULL) { + $service = new static( + $this->configFactory, + $this->currentUser, + $this->languageManager, + $this->pagerManager, + $this->pagerParameters, + $this->apiClient, + $this->requestStack, + $this->renderer, + $this->stringTranslation, + $this->entityTypeManager + ); + $service->setParameters(Parameters::createFromUrl($url)); + return $service; + } + /** * {@inheritdoc} */ From fbbc82a31f969150a683bc2844fb78f0ba6b29a9 Mon Sep 17 00:00:00 2001 From: orakili Date: Tue, 14 Feb 2023 06:39:38 +0000 Subject: [PATCH 4/6] fix: return proper value Refs: RW-700 --- html/modules/custom/reliefweb_rivers/src/RiverServiceBase.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/html/modules/custom/reliefweb_rivers/src/RiverServiceBase.php b/html/modules/custom/reliefweb_rivers/src/RiverServiceBase.php index e29006f75..0f99355e9 100644 --- a/html/modules/custom/reliefweb_rivers/src/RiverServiceBase.php +++ b/html/modules/custom/reliefweb_rivers/src/RiverServiceBase.php @@ -1217,7 +1217,7 @@ public static function getRiverServiceFromUrl($url) { } return $instances[$url] ?? NULL; } - return []; + return NULL; } /** From 3ff3c002c715f9cef4df02e7f6fb5470fb3d414c Mon Sep 17 00:00:00 2001 From: orakili Date: Tue, 14 Feb 2023 09:02:54 +0000 Subject: [PATCH 5/6] chore: make sure RiverServiceInterface matches RiverServiceBase Refs: RW-700 --- .../reliefweb_rivers/src/RiverServiceBase.php | 19 +-------- .../src/RiverServiceInterface.php | 40 +++++++++++++++++++ .../src/Services/TopicRiver.php | 8 ++-- 3 files changed, 46 insertions(+), 21 deletions(-) diff --git a/html/modules/custom/reliefweb_rivers/src/RiverServiceBase.php b/html/modules/custom/reliefweb_rivers/src/RiverServiceBase.php index 0f99355e9..3028308c4 100644 --- a/html/modules/custom/reliefweb_rivers/src/RiverServiceBase.php +++ b/html/modules/custom/reliefweb_rivers/src/RiverServiceBase.php @@ -1139,15 +1139,7 @@ public static function getRiverService($bundle) { } /** - * Get the river path to river data mapping. - * - * This notably handles the legacy river paths like "maps". - * - * @return array - * Mapping with the river path as key and an array with the entity bundle - * associated with the river and an optional view for legacy paths. - * - * @todo check if nginx handles the redirections correctly. + * {@inheritdoc} */ public static function getRiverMapping() { return [ @@ -1184,14 +1176,7 @@ public static function getRiverMapping() { } /** - * Get the river service from a river URL. - * - * @param string $url - * River url. - * - * @return \Drupal\reliefweb_rivers\RiverServiceInferface|null - * If there is a match, then the service for the corresponding river, - * NULL otherwise. + * {@inheritdoc} */ public static function getRiverServiceFromUrl($url) { static $instances = []; diff --git a/html/modules/custom/reliefweb_rivers/src/RiverServiceInterface.php b/html/modules/custom/reliefweb_rivers/src/RiverServiceInterface.php index 4e68b960a..a9047cf96 100644 --- a/html/modules/custom/reliefweb_rivers/src/RiverServiceInterface.php +++ b/html/modules/custom/reliefweb_rivers/src/RiverServiceInterface.php @@ -252,6 +252,20 @@ public function getRiverSearch(); */ public function getRiverAdvancedSearch(); + /** + * Get the river content render array. + * + * @return array + * Render array with the river content including: + * - id + * - title + * - results (see ::getRiverResults()) + * - entities + * - pager (see ::getRiverPager()) + * - empty message. + */ + public function getRiverContent(); + /** * Get the river results render array. * @@ -466,6 +480,19 @@ public static function createDate($date); */ public static function getRiverUrl($bundle, array $parameters = [], $title = '', $partial_title = FALSE, $absolute = FALSE); + /** + * Generate a river title from the given entity bundle and title prefix. + * + * @param string $bundle + * Entity bundle of the river. + * @param string $prefix + * Title prefix. + * + * @return string + * River title. + */ + public static function getRiverUrlTitle($bundle, $prefix); + /** * Get the data of the river for given bundle and API data. * @@ -509,6 +536,19 @@ public static function getRiverApiPayload($bundle, $view = '', array $exclude = */ public static function getRiverService($bundle); + /** + * Get the river path to river data mapping. + * + * This notably handles the legacy river paths like "maps". + * + * @return array + * Mapping with the river path as key and an array with the entity bundle + * associated with the river and an optional view for legacy paths. + * + * @todo check if nginx handles the redirections correctly. + */ + public static function getRiverMapping(); + /** * Get the river service from a river URL. * diff --git a/html/modules/custom/reliefweb_rivers/src/Services/TopicRiver.php b/html/modules/custom/reliefweb_rivers/src/Services/TopicRiver.php index 07da69df8..9d2398495 100644 --- a/html/modules/custom/reliefweb_rivers/src/Services/TopicRiver.php +++ b/html/modules/custom/reliefweb_rivers/src/Services/TopicRiver.php @@ -52,15 +52,15 @@ class TopicRiver extends RiverServiceBase { /** * {@inheritdoc} */ - public function getDefaultPageTitle() { - return $this->t('Topics'); + public function getPageTitle() { + return $this->getDefaultPageTitle(); } /** * {@inheritdoc} */ - public function getPageTitle() { - return $this->getDefaultPageTitle(); + public function getDefaultPageTitle() { + return $this->t('Topics'); } /** From 28ddba3c16ea5b41155d8a865d6ed1613da2dd72 Mon Sep 17 00:00:00 2001 From: orakili Date: Tue, 14 Feb 2023 09:58:22 +0000 Subject: [PATCH 6/6] fix: prevent issue when creating site from scratch Refs: RW-700 --- .../reliefweb_rivers/src/AdvancedSearch.php | 2 +- .../reliefweb_rivers/src/RiverServiceBase.php | 26 ++++++++++--------- .../src/Services/TopicRiver.php | 1 + 3 files changed, 16 insertions(+), 13 deletions(-) diff --git a/html/modules/custom/reliefweb_rivers/src/AdvancedSearch.php b/html/modules/custom/reliefweb_rivers/src/AdvancedSearch.php index 4f57b0285..cd51660b0 100644 --- a/html/modules/custom/reliefweb_rivers/src/AdvancedSearch.php +++ b/html/modules/custom/reliefweb_rivers/src/AdvancedSearch.php @@ -216,7 +216,7 @@ protected function computeAdvancedSearchData() { // available. $remove = RiverServiceBase::getRiverUrl( $this->bundle, - $this->parameters->getAll(['advanced-search']) + $this->parameters->getAllSorted(['advanced-search']) ); // Sanitize the advanced search parameter for the entire selection. diff --git a/html/modules/custom/reliefweb_rivers/src/RiverServiceBase.php b/html/modules/custom/reliefweb_rivers/src/RiverServiceBase.php index 3028308c4..c873fdb7a 100644 --- a/html/modules/custom/reliefweb_rivers/src/RiverServiceBase.php +++ b/html/modules/custom/reliefweb_rivers/src/RiverServiceBase.php @@ -180,7 +180,6 @@ public function __construct( $this->requestStack = $request_stack; $this->renderer = $renderer; $this->stringTranslation = $string_translation; - $this->url = static::getRiverUrl($this->getBundle()); } /** @@ -264,6 +263,9 @@ public function getResource() { * {@inheritdoc} */ public function getUrl() { + if (!isset($this->url)) { + $this->url = static::getRiverUrl($this->getBundle()); + } return $this->url; } @@ -1027,22 +1029,22 @@ public static function createDate($date) { * {@inheritdoc} */ public static function getRiverUrl($bundle, array $parameters = [], $title = '', $partial_title = FALSE, $absolute = FALSE) { - try { - $url = Url::fromRoute('reliefweb_rivers.' . $bundle . '.river', []); - } - catch (RouteNotFoundException $exception) { - return ''; - } - $title = !empty($partial_title) ? static::getRiverUrlTitle($bundle, $title) : $title; if (!empty($title)) { + // Set it as first parameter for consistent order of parameters and to + // make it more user friendly. $parameters = ['list' => $title] + $parameters; } - return $url - ->setOption('query', $parameters) - ->setOption('absolute', $absolute) - ->toString(); + try { + return Url::fromRoute('reliefweb_rivers.' . $bundle . '.river', [], [ + 'query' => $parameters, + 'absolute' => $absolute, + ])->toString(); + } + catch (RouteNotFoundException $exception) { + return ''; + } } /** diff --git a/html/modules/custom/reliefweb_rivers/src/Services/TopicRiver.php b/html/modules/custom/reliefweb_rivers/src/Services/TopicRiver.php index 9d2398495..c63d45c17 100644 --- a/html/modules/custom/reliefweb_rivers/src/Services/TopicRiver.php +++ b/html/modules/custom/reliefweb_rivers/src/Services/TopicRiver.php @@ -265,6 +265,7 @@ public function getApiPayload($view = '') { */ public function parseApiData(array $api_data, $view = '') { // Not used. + return []; } /**