Updated Drupal to 8.6. This goes with the following updates because it's possible...
[yaffs-website] / web / core / lib / Drupal / Core / Routing / RequestFormatRouteFilter.php
index dbfb801b7856de897ef08c887f79690da9612017..fad7bc04659f0ef4dedc87741a08eae75bfcb79b 100644 (file)
@@ -2,8 +2,10 @@
 
 namespace Drupal\Core\Routing;
 
+use Drupal\Core\Url;
 use Symfony\Component\HttpFoundation\Request;
 use Symfony\Component\HttpKernel\Exception\NotAcceptableHttpException;
+use Symfony\Component\Routing\Route;
 use Symfony\Component\Routing\RouteCollection;
 
 /**
@@ -17,7 +19,14 @@ class RequestFormatRouteFilter implements FilterInterface {
   public function filter(RouteCollection $collection, Request $request) {
     // Determine the request format.
     $default_format = static::getDefaultFormat($collection);
-    $format = $request->getRequestFormat($default_format);
+    // If the request does not specify a format then use the default.
+    if (is_null($request->getRequestFormat(NULL))) {
+      $format = $default_format;
+      $request->setRequestFormat($default_format);
+    }
+    else {
+      $format = $request->getRequestFormat($default_format);
+    }
 
     $routes_with_requirement = [];
     $routes_without_requirement = [];
@@ -52,7 +61,18 @@ class RequestFormatRouteFilter implements FilterInterface {
     // We do not throw a
     // \Symfony\Component\Routing\Exception\ResourceNotFoundException here
     // because we don't want to return a 404 status code, but rather a 406.
-    throw new NotAcceptableHttpException("No route found for the specified format $format.");
+    $available_formats = static::getAvailableFormats($collection);
+    $not_acceptable = new NotAcceptableHttpException("No route found for the specified format $format. Supported formats: " . implode(', ', $available_formats) . '.');
+    if ($available_formats) {
+      $links = [];
+      foreach ($available_formats as $available_format) {
+        $url = Url::fromUri($request->getUri(), ['query' => ['_format' => $available_format]])->toString(TRUE)->getGeneratedUrl();
+        $content_type = $request->getMimeType($available_format);
+        $links[] = "<$url>; rel=\"alternate\"; type=\"$content_type\"";
+      }
+      $not_acceptable->setHeaders(['Link' => implode(', ', $links)]);
+    }
+    throw $not_acceptable;
   }
 
   /**
@@ -60,7 +80,9 @@ class RequestFormatRouteFilter implements FilterInterface {
    *
    * By default, use 'html' as the default format. But when there's only a
    * single route match, and that route specifies a '_format' requirement
-   * listing a single format, then use that as the default format.
+   * listing a single format, then use that as the default format. Also, if
+   * there are multiple routes which all require the same single format then
+   * use it.
    *
    * @param \Symfony\Component\Routing\RouteCollection $collection
    *   The route collection to filter.
@@ -69,15 +91,32 @@ class RequestFormatRouteFilter implements FilterInterface {
    *   The default format.
    */
   protected static function getDefaultFormat(RouteCollection $collection) {
-    $default_format = 'html';
-    if ($collection->count() === 1) {
-      $only_route = $collection->getIterator()->current();
-      $required_format = $only_route->getRequirement('_format');
-      if (strpos($required_format, '|') === FALSE) {
-        $default_format = $required_format;
-      }
-    }
-    return $default_format;
+    $formats = static::getAvailableFormats($collection);
+
+    // The default format is 'html' unless ALL routes require the same format.
+    return count($formats) === 1
+      ? reset($formats)
+      : 'html';
+  }
+
+  /**
+   * Gets the set of formats across all routes in the collection.
+   *
+   * @param \Symfony\Component\Routing\RouteCollection $collection
+   *   The route collection to filter.
+   *
+   * @return string[]
+   *   All available formats.
+   */
+  protected static function getAvailableFormats(RouteCollection $collection) {
+    $all_formats = array_reduce($collection->all(), function (array $carry, Route $route) {
+      // Routes without a '_format' requirement are assumed to require HTML.
+      $route_formats = !$route->hasRequirement('_format')
+        ? ['html']
+        : explode('|', $route->getRequirement('_format'));
+      return array_merge($carry, $route_formats);
+    }, []);
+    return array_unique(array_filter($all_formats));
   }
 
 }