diff --git a/CHANGELOG.md b/CHANGELOG.md index ea3d60488..60933af6c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,18 @@ +# v1.6.17 +## 10/06/2019 + +1. [](#new) + * Added working ETag (304 Not Modified) support based on the final rendered HTML +1. [](#improved) + * Safer file handling + customizable null char replacement in `CsvFormatter::decode()` + * Change of Behavior: `Inflector::hyphenize` will now automatically trim dashes at beginning and end of a string. + * Change in Behavior for `Folder::all()` so no longer fails if trying to copy non-existent dot file [#2581](https://github.com/getgrav/grav/pull/2581) + * renamed composer `test-plugins` script to `phpstan-plugins` to be more explicit [#2637](https://github.com/getgrav/grav/pull/2637) +1. [](#bugfix) + * Fixed PHP 7.1 bug in FlexMedia + * Fix cache image generation when using cropResize [#2639](https://github.com/getgrav/grav/pull/2639) + * Fix `array_merge()` exception with non-array page header metadata [#2701](https://github.com/getgrav/grav/pull/2701) + # v1.6.16 ## 09/19/2019 diff --git a/composer.json b/composer.json index 60ca21a61..34b4c8d18 100644 --- a/composer.json +++ b/composer.json @@ -2,7 +2,13 @@ "name": "getgrav/grav", "type": "project", "description": "Modern, Crazy Fast, Ridiculously Easy and Amazingly Powerful Flat-File CMS", - "keywords": ["cms","flat-file cms","flat cms","flatfile cms","php"], + "keywords": [ + "cms", + "flat-file cms", + "flat cms", + "flatfile cms", + "php" + ], "homepage": "https://getgrav.org", "license": "MIT", "require": { @@ -16,14 +22,11 @@ "symfony/polyfill-iconv": "^1.9", "symfony/polyfill-php72": "^1.9", "symfony/polyfill-php73": "^1.9", - "psr/simple-cache": "^1.0", "psr/http-message": "^1.0", "psr/http-server-middleware": "^1.0", - "kodus/psr7-server": "*", "nyholm/psr7": "^1.0", - "twig/twig": "~1.40", "erusev/parsedown": "1.6.4", "erusev/parsedown-extra": "~0.7", @@ -36,7 +39,6 @@ "doctrine/collections": "^1.5", "guzzlehttp/psr7": "^1.4", "filp/whoops": "~2.2", - "matthiasmullie/minify": "^1.3", "monolog/monolog": "~1.0", "gregwar/image": "2.*", @@ -83,10 +85,14 @@ "psr-4": { "Grav\\": "system/src/Grav" }, - "files": ["system/defines.php"] + "files": [ + "system/defines.php" + ] }, "archive": { - "exclude": ["VERSION"] + "exclude": [ + "VERSION" + ] }, "scripts": { "api-16": "vendor/bin/phpdoc-md generate system/src > user/pages/14.api/default.16.md", @@ -94,7 +100,7 @@ "post-create-project-cmd": "bin/grav install", "phpstan": "vendor/bin/phpstan analyse -l 2 -c ./tests/phpstan/phpstan.neon system/src --memory-limit=256M", "phpstan-framework": "vendor/bin/phpstan analyse -l 5 -c ./tests/phpstan/phpstan.neon system/src/Grav/Framework --memory-limit=256M", - "test-plugins": "vendor/bin/phpstan analyse -l 0 -c ./tests/phpstan/plugins.neon user/plugins --memory-limit=256M", + "phpstan-plugins": "vendor/bin/phpstan analyse -l 0 -c ./tests/phpstan/plugins.neon user/plugins --memory-limit=256M", "test": "vendor/bin/codecept run unit", "test-windows": "vendor\\bin\\codecept run unit" }, diff --git a/system/defines.php b/system/defines.php index 85d88d8f7..5a283c40a 100644 --- a/system/defines.php +++ b/system/defines.php @@ -8,7 +8,7 @@ // Some standard defines define('GRAV', true); -define('GRAV_VERSION', '1.6.16'); +define('GRAV_VERSION', '1.6.17'); define('GRAV_TESTING', false); define('DS', '/'); diff --git a/system/src/Grav/Common/Filesystem/Folder.php b/system/src/Grav/Common/Filesystem/Folder.php index f17410c49..2a38d88e5 100644 --- a/system/src/Grav/Common/Filesystem/Folder.php +++ b/system/src/Grav/Common/Filesystem/Folder.php @@ -235,7 +235,7 @@ abstract class Folder /** @var \RecursiveDirectoryIterator $file */ foreach ($iterator as $file) { // Ignore hidden files. - if (strpos($file->getFilename(), '.') === 0) { + if (strpos($file->getFilename(), '.') === 0 && $file->isFile()) { continue; } if (!$folders && $file->isDir()) { diff --git a/system/src/Grav/Common/Grav.php b/system/src/Grav/Common/Grav.php index 009d57de5..9cb74cc18 100644 --- a/system/src/Grav/Common/Grav.php +++ b/system/src/Grav/Common/Grav.php @@ -247,9 +247,21 @@ class Grav extends Container $collection = new RequestHandler($this->middleware, $default, $container); $response = $collection->handle($this['request']); + $body = $response->getBody(); + + // Handle ETag and If-None-Match headers. + if ($response->getHeaderLine('ETag') === '1') { + $etag = md5($body); + $response = $response->withHeader('ETag', $etag); + + if ($this['request']->getHeaderLine('If-None-Match') === $etag) { + $response = $response->withStatus(304); + $body = ''; + } + } $this->header($response); - echo $response->getBody(); + echo $body; $debugger->render(); diff --git a/system/src/Grav/Common/Inflector.php b/system/src/Grav/Common/Inflector.php index a24bbfd84..eca93c197 100644 --- a/system/src/Grav/Common/Inflector.php +++ b/system/src/Grav/Common/Inflector.php @@ -193,6 +193,8 @@ class Inflector $regex3 = preg_replace('/([0-9])([A-Z])/', '\1-\2', $regex2); $regex4 = preg_replace('/[^A-Z^a-z^0-9]+/', '-', $regex3); + $regex4 = trim($regex4, '-'); + return strtolower($regex4); } diff --git a/system/src/Grav/Common/Media/Traits/MediaTrait.php b/system/src/Grav/Common/Media/Traits/MediaTrait.php index c65f87da6..dc6550dbf 100644 --- a/system/src/Grav/Common/Media/Traits/MediaTrait.php +++ b/system/src/Grav/Common/Media/Traits/MediaTrait.php @@ -125,5 +125,5 @@ trait MediaTrait /** * @return string */ - abstract protected function getCacheKey(); + abstract protected function getCacheKey(): string; } diff --git a/system/src/Grav/Common/Page/Page.php b/system/src/Grav/Common/Page/Page.php index 173035981..ea10cd47b 100644 --- a/system/src/Grav/Common/Page/Page.php +++ b/system/src/Grav/Common/Page/Page.php @@ -529,9 +529,9 @@ class Page implements PageInterface $headers['Last-Modified'] = $last_modified_date; } - // Calculate ETag based on the raw file + // Ask Grav to calculate ETag from the final content. if ($this->eTag()) { - $headers['ETag'] = '"' . md5($this->raw() . $this->modified()).'"'; + $headers['ETag'] = '1'; } // Set Vary: Accept-Encoding header @@ -1200,7 +1200,7 @@ class Page implements PageInterface /** * @return string */ - protected function getCacheKey() + protected function getCacheKey(): string { return $this->id(); } @@ -1696,7 +1696,7 @@ class Page implements PageInterface // Get initial metadata for the page $metadata = array_merge($metadata, Grav::instance()['config']->get('site.metadata')); - if (isset($this->header->metadata)) { + if (isset($this->header->metadata) && is_array($this->header->metadata)) { // Merge any site.metadata settings in with page metadata $metadata = array_merge($metadata, $this->header->metadata); } diff --git a/system/src/Grav/Framework/File/Formatter/CsvFormatter.php b/system/src/Grav/Framework/File/Formatter/CsvFormatter.php index 8945df004..6c1c3b4f4 100644 --- a/system/src/Grav/Framework/File/Formatter/CsvFormatter.php +++ b/system/src/Grav/Framework/File/Formatter/CsvFormatter.php @@ -79,10 +79,28 @@ class CsvFormatter extends AbstractFormatter // Get the field names $header = str_getcsv(array_shift($lines), $delimiter); + // Allow for replacing a null string with null/empty value + $null_replace = $this->getConfig('null'); + // Get the data $list = []; - foreach ($lines as $line) { - $list[] = array_combine($header, str_getcsv($line, $delimiter)); + $line = null; + try { + foreach ($lines as $line) { + if (!empty($line)) { + $csv_line = str_getcsv($line, $delimiter); + + if ($null_replace) { + array_walk($csv_line, function(&$el) use ($null_replace) { + $el = str_replace($null_replace, null, $el); + }); + } + + $list[] = array_combine($header, $csv_line); + } + } + } catch (\Exception $e) { + throw new \Exception('Badly formatted CSV line: ' . $line); } return $list; diff --git a/system/src/Grav/Framework/Flex/Traits/FlexMediaTrait.php b/system/src/Grav/Framework/Flex/Traits/FlexMediaTrait.php index 8b2270604..c894634c6 100644 --- a/system/src/Grav/Framework/Flex/Traits/FlexMediaTrait.php +++ b/system/src/Grav/Framework/Flex/Traits/FlexMediaTrait.php @@ -342,5 +342,5 @@ trait FlexMediaTrait abstract public function getFlexDirectory(): FlexDirectory; - abstract public function getStorageKey(); + abstract public function getStorageKey(): string; } diff --git a/tests/unit/Grav/Common/Markdown/ParsedownTest.php b/tests/unit/Grav/Common/Markdown/ParsedownTest.php index 58a1acf61..ede6736f7 100644 --- a/tests/unit/Grav/Common/Markdown/ParsedownTest.php +++ b/tests/unit/Grav/Common/Markdown/ParsedownTest.php @@ -110,6 +110,7 @@ class ParsedownTest extends \Codeception\TestCase\Test public function testImagesSubDir() { + $this->config->set('system.images.cache_all', false); $this->uri->initializeWithUrlAndRootPath('http://testing.dev/subdir/item2/item2-2', '/subdir')->init(); $this->assertRegexp('|
<\/p>|',
diff --git a/webserver-configs/nginx-ddev-site.conf b/webserver-configs/nginx-ddev-site.conf
index e75cf2ff1..be3cf3721 100644
--- a/webserver-configs/nginx-ddev-site.conf
+++ b/webserver-configs/nginx-ddev-site.conf
@@ -2,7 +2,7 @@
# You can override ddev's configuration by placing an edited copy
# of this config (or one of the other ones) in .ddev/nginx-site.conf
-# See https://ddev.readthedocs.io/en/latest/users/extend/customization-extendibility/#providing-custom-nginx-configuration
+# See https://ddev.readthedocs.io/en/stable/users/extend/customization-extendibility/#providing-custom-nginx-configuration
# Set https to 'on' if x-forwarded-proto is https
map $http_x_forwarded_proto $fcgi_https {
@@ -11,11 +11,16 @@ map $http_x_forwarded_proto $fcgi_https {
}
server {
- listen 80; ## listen for ipv4; this line is default and implied
- listen [::]:80 default ipv6only=on; ## listen for ipv6
- # The NGINX_DOCROOT variable is substituted with
+ listen 80;
+ listen [::]:80 default ipv6only=on;
+
+ # The WEBSERVER_DOCROOT variable is substituted with
# its value when the container is started.
- root $NGINX_DOCROOT;
+ root $WEBSERVER_DOCROOT;
+
+ include /etc/nginx/monitoring.conf;
+
+
index index.php index.htm index.html;
# Make site accessible from http://localhost/
@@ -23,15 +28,20 @@ server {
# Disable sendfile as per https://docs.vagrantup.com/v2/synced-folders/virtualbox.html
sendfile off;
- error_log /var/log/nginx/error.log info;
+ error_log /dev/stdout info;
access_log /var/log/nginx/access.log;
+ ## Begin - Index
+ # for subfolders, simply adjust:
+ # `location /subfolder {`
+ # and the rewrite to use `/subfolder/index.php`
location / {
- absolute_redirect off;
try_files $uri $uri/ /index.php?$query_string;
}
+ ## End - Index
- # pass the PHP scripts to FastCGI server listening on socket
+
+ # pass the PHP scripts to FastCGI server listening on socket
location ~ \.php$ {
try_files $uri =404;
fastcgi_split_path_info ^(.+\.php)(/.+)$;
@@ -42,38 +52,78 @@ server {
fastcgi_param SCRIPT_NAME $fastcgi_script_name;
fastcgi_index index.php;
include fastcgi_params;
- fastcgi_intercept_errors on;
+ fastcgi_intercept_errors off;
# fastcgi_read_timeout should match max_execution_time in php.ini
fastcgi_read_timeout 10m;
fastcgi_param SERVER_NAME $host;
fastcgi_param HTTPS $fcgi_https;
}
- # Expire rules for static content
- # Feed
- location ~* \.(?:rss|atom|cache)$ {
- expires 1h;
- }
+ ## Begin - Security
+ # deny all direct access for these folders
+ location ~* /(\.git|cache|bin|logs|backup|tests)/.*$ { return 403; }
+ # deny running scripts inside core system folders
+ location ~* /(system|vendor)/.*\.(txt|xml|md|html|yaml|yml|php|pl|py|cgi|twig|sh|bat)$ { return 403; }
+ # deny running scripts inside user folder
+ location ~* /user/.*\.(txt|md|yaml|yml|php|pl|py|cgi|twig|sh|bat)$ { return 403; }
+ # deny access to specific files in the root folder
+ location ~ /(LICENSE\.txt|composer\.lock|composer\.json|nginx\.conf|web\.config|htaccess\.txt|\.htaccess) { return 403; }
+ ## End - Security
- # Media: images, icons, video, audio, HTC
- location ~* \.(?:jpg|jpeg|gif|png|ico|cur|gz|svg|svgz|mp4|ogg|ogv|webm|htc)$ {
- expires 1M;
- access_log off;
- add_header Cache-Control "public";
- }
+ include /mnt/ddev_config/nginx/*.conf;
+}
- # Prevent clients from accessing hidden files (starting with a dot)
- # This is particularly important if you store .htpasswd files in the site hierarchy
- # Access to `/.well-known/` is allowed.
- # https://www.mnot.net/blog/2010/04/07/well-known
- # https://tools.ietf.org/html/rfc5785
- location ~* /\.(?!well-known\/) {
- deny all;
- }
- # Prevent clients from accessing to backup/config/source files
- location ~* (?:\.(?:bak|conf|dist|fla|in[ci]|log|psd|sh|sql|sw[op])|~)$ {
- deny all;
+server {
+ listen 443 ssl;
+ listen [::]:443 default ipv6only=on;
+
+ # The WEBSERVER_DOCROOT variable is substituted with
+ # its value when the container is started.
+ root $WEBSERVER_DOCROOT;
+
+ ssl_certificate /etc/ssl/certs/master.crt;
+ ssl_certificate_key /etc/ssl/certs/master.key;
+
+ include /etc/nginx/monitoring.conf;
+
+
+ index index.php index.htm index.html;
+
+ # Make site accessible from http://localhost/
+ server_name _;
+
+ # Disable sendfile as per https://docs.vagrantup.com/v2/synced-folders/virtualbox.html
+ sendfile off;
+ error_log /dev/stdout info;
+ access_log /var/log/nginx/access.log;
+
+ ## Begin - Index
+ # for subfolders, simply adjust:
+ # `location /subfolder {`
+ # and the rewrite to use `/subfolder/index.php`
+ location / {
+ try_files $uri $uri/ /index.php?$query_string;
+ }
+ ## End - Index
+
+
+ # pass the PHP scripts to FastCGI server listening on socket
+ location ~ \.php$ {
+ try_files $uri =404;
+ fastcgi_split_path_info ^(.+\.php)(/.+)$;
+ fastcgi_pass unix:/run/php-fpm.sock;
+ fastcgi_buffers 16 16k;
+ fastcgi_buffer_size 32k;
+ fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
+ fastcgi_param SCRIPT_NAME $fastcgi_script_name;
+ fastcgi_index index.php;
+ include fastcgi_params;
+ fastcgi_intercept_errors off;
+ # fastcgi_read_timeout should match max_execution_time in php.ini
+ fastcgi_read_timeout 10m;
+ fastcgi_param SERVER_NAME $host;
+ fastcgi_param HTTPS $fcgi_https;
}
## Begin - Security
@@ -88,31 +138,5 @@ server {
## End - Security
- ## provide a health check endpoint
- location /healthcheck {
- access_log off;
- stub_status on;
- keepalive_timeout 0; # Disable HTTP keepalive
- return 200;
- }
-
- error_page 400 401 /40x.html;
- location = /40x.html {
- root /usr/share/nginx/html;
- }
-
- location ~ ^/(fpmstatus|ping)$ {
- access_log off;
- stub_status on;
- keepalive_timeout 0; # Disable HTTP keepalive
- allow 127.0.0.1;
- allow all;
- fastcgi_index index.php;
- fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
- include fastcgi_params;
- fastcgi_pass unix:/run/php-fpm.sock;
- }
-
-
+ include /mnt/ddev_config/nginx/*.conf;
}
-