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; } -