Supporting negotiated content types

This commit is contained in:
Andy Miller
2018-11-11 18:27:12 -07:00
parent ecbc401584
commit 74cd3ac1e0
5 changed files with 156 additions and 45 deletions

View File

@@ -48,7 +48,8 @@
"miljar/php-exif": "^0.6.4",
"composer/ca-bundle": "^1.0",
"dragonmantank/cron-expression": "^1.2",
"phive/twig-extensions-deferred": "^1.0"
"phive/twig-extensions-deferred": "^1.0",
"willdurand/negotiation": "^2.3"
},
"require-dev": {
"codeception/codeception": "^2.4",

54
composer.lock generated
View File

@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "a6592549298919c71cbcd8007b976e85",
"content-hash": "0d5507fb2bcbb3cf1cb9c51f8df81397",
"packages": [
{
"name": "antoligy/dom-string-iterators",
@@ -2388,6 +2388,58 @@
"templating"
],
"time": "2018-07-13T07:12:17+00:00"
},
{
"name": "willdurand/negotiation",
"version": "v2.3.1",
"source": {
"type": "git",
"url": "https://github.com/willdurand/Negotiation.git",
"reference": "03436ededa67c6e83b9b12defac15384cb399dc9"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/willdurand/Negotiation/zipball/03436ededa67c6e83b9b12defac15384cb399dc9",
"reference": "03436ededa67c6e83b9b12defac15384cb399dc9",
"shasum": ""
},
"require": {
"php": ">=5.4.0"
},
"require-dev": {
"phpunit/phpunit": "~4.5"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.3-dev"
}
},
"autoload": {
"psr-4": {
"Negotiation\\": "src/Negotiation"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "William Durand",
"email": "will+git@drnd.me"
}
],
"description": "Content Negotiation tools for PHP provided as a standalone library.",
"homepage": "http://williamdurand.fr/Negotiation/",
"keywords": [
"accept",
"content",
"format",
"header",
"negotiation"
],
"time": "2017-05-14T17:21:12+00:00"
}
],
"packages-dev": [

View File

@@ -53,7 +53,7 @@ pages:
special_chars: # List of special characters to automatically convert to entities
'>': 'gt'
'<': 'lt'
types: [txt,xml,html,htm,json,rss,atom] # list of valid page types
types: [html,htm,json,xml,txt,rss,atom] # list of valid page types
append_url_extension: '' # Append page's extension in Page urls (e.g. '.html' results in /path/page.html)
expires: 604800 # Page expires time in seconds (604800 seconds = 7 days)
cache_control: # Can be blank for no setting, or a valid `cache-control` text value

View File

@@ -23,6 +23,7 @@ use Grav\Common\Taxonomy;
use Grav\Common\Uri;
use Grav\Common\Utils;
use Grav\Common\Yaml;
use Negotiation\Negotiator;
use RocketTheme\Toolbox\Event\Event;
use RocketTheme\Toolbox\File\MarkdownFile;
@@ -1287,12 +1288,31 @@ class Page implements PageInterface
$this->template_format = $var;
}
// Set from URL extension set on page
if (empty($this->template_format)) {
$this->template_format = $this->url_extension;
}
// Set from uri extension
if (empty($this->template_format)) {
$this->template_format = Grav::instance()['uri']->extension('html');
$this->template_format = Grav::instance()['uri']->extension();
}
// Use content negotitation via the `accept:` header
if (empty($this->template_format) && $accept = $_SERVER['HTTP_ACCEPT'] ?? false) {
$negotiator = new Negotiator();
$supported_types = Grav::instance()['config']->get('system.pages.types', ['html', 'json']);
$priorities = Utils::getMimeTypes($supported_types);
$media_type = $negotiator->getBest($accept, $priorities);
$mimetype = $media_type->getValue();
$this->template_format = Utils::getExtensionByMime($mimetype);
}
// Last chance set a default type
if (empty($this->template_format)) {
$this->template_format = 'html';
}
return $this->template_format;

View File

@@ -478,6 +478,85 @@ abstract class Utils
return $default;
}
/**
* Get all the mimetypes for an array of extensions
*
* @param array $extensions
* @return array
*/
public static function getMimeTypes(array $extensions)
{
$mimetypes = [];
foreach ($extensions as $extension) {
$mimetype = static::getMimeByExtension($extension, false);
if ($mimetype && !in_array($mimetype, $mimetypes)) {
$mimetypes[] = $mimetype;
}
}
return $mimetypes;
}
/**
* Return the mimetype based on filename extension
*
* @param string $mime mime type (eg "text/html")
* @param string $default default value
*
* @return string
*/
public static function getExtensionByMime($mime, $default = 'html')
{
$mime = strtolower($mime);
// look for some standard mime types
switch ($mime) {
case '*/*':
case 'text/*':
case 'text/html':
return 'html';
case 'application/json':
return 'json';
case 'application/atom+xml':
return 'atom';
case 'application/rss+xml':
return 'rss';
case 'application/xml':
return 'xml';
}
$media_types = (array)Grav::instance()['config']->get('media.types');
foreach ($media_types as $extension => $type) {
if ($extension === 'defaults') {
continue;
}
if (isset($type['mime']) && $type['mime'] === $mime) {
return $extension;
}
}
return $default;
}
/**
* Get all the extensions for an array of mimetypes
*
* @param array $mimetypes
* @return array
*/
public static function getExtensions(array $mimetypes)
{
$extensions = [];
foreach ($mimetypes as $mimetype) {
$extension = static::getExtensionByMime($mimetype, false);
if ($extension && !in_array($extension, $extensions)) {
$extensions[] = $extension;
}
}
return $extensions;
}
/**
* Return the mimetype based on filename
*
@@ -523,47 +602,6 @@ abstract class Utils
return $type ?: static::getMimeByFilename($filename, $default);
}
/**
* Return the mimetype based on filename extension
*
* @param string $mime mime type (eg "text/html")
* @param string $default default value
*
* @return string
*/
public static function getExtensionByMime($mime, $default = 'html')
{
$mime = strtolower($mime);
// look for some standard mime types
switch ($mime) {
case '*/*':
case 'text/*':
case 'text/html':
return 'html';
case 'application/json':
return 'json';
case 'application/atom+xml':
return 'atom';
case 'application/rss+xml':
return 'rss';
case 'application/xml':
return 'xml';
}
$media_types = (array)Grav::instance()['config']->get('media.types');
foreach ($media_types as $extension => $type) {
if ($extension === 'defaults') {
continue;
}
if (isset($type['mime']) && $type['mime'] === $mime) {
return $extension;
}
}
return $default;
}
/**
* Returns true if filename is considered safe.