Merge branch 'develop' into feature/introduce-testing

This commit is contained in:
Flavio Copes
2016-01-07 13:03:06 +01:00
7 changed files with 259 additions and 80 deletions

View File

@@ -1,10 +1,43 @@
# v1.0.7
## 01/XX/2016
1. [](#new)
* Added `composer create-project` as an additional installation method #585
* New optional system config setting to strip home from page routs and urls #561
* Added Greek, Finnish, Norwegian, Polish, Portuguese, and Romanian languages
* Added new `Page->topParent()` method to return top most parent of a page
* Added plugins configuration tab to debugger
* Added support for APCu and PHP7.0 via new Doctrine Cache release
* Added global setting for `twig_first` processing (false by default)
* New configuration options for Session settings #553
1. [](#improved)
* Use `URI->host()` for session domain
* Add support for `open_basedir` when installing packages via GPM
* Improved `Utils::generateNonceString()` method to handle reverse proxies
* Optimized core thumbnails saving 38% in file size
* Added new `bin/gpm index --installed-only` option
* Improved GPM errors to provider more helpful diagnostic of issues
* Removed old hardcoded PHP version references
* Moved `onPageContentProcessed()` event so it's fired more reliably
* Maintain md5 keys during sorting of Assets #566
* Update to Caddyfile for Caddy web server
1. [](#bugfix)
* Fixed an issue with cache/config checksum not being set on cache load
* Fix for page blueprint and theme inheritance issue #534
* Set `ZipBackup` timeout to 10 minutes if possible
* Fix case where we only have inline data for CSS or JS #565
* Fix `bin/grav sandbox` command to work with new `webserver-config` folder
* Fix for markdown attributes on external URLs
* Fixed issue where `data:` page header was acting as `publish_date:`
* Fix for special characters in URL parameters (e.g. /tag:c++) #541
# v1.0.6
## 12/22/2015
1. [](#new)
* Set minimum requirements to [PHP 5.5.9](http://bit.ly/1Jt9OXO)
* Set minimum requirements to [PHP 5.5.9](http://bit.ly/1Jt9OXO)
* Added `saveConfig` to Themes
1. [](#improved)
1. [](#improved)
* Updated Whoops to new 2.0 version (PHP 7.0 compatible)
* Moved sample web server configs into dedicated directory
* FastCGI will use Apache's `mod_deflate` if gzip turned off
@@ -14,7 +47,7 @@
* Fix lang prefix in url twig variables #523
* Fix case insensitive HTTPS check #535
* Field field validation handles case `multiple` missing
# v1.0.5
## 12/18/2015
@@ -25,7 +58,7 @@
* Use Grav's fork of Parsedown until PR is merged
* New function to persist plugin configuration to disk
* GPM `selfupgrade` will now check PHP version requirements
1. [](#improved)
1. [](#improved)
* If the field allows multiple files, return array
* Handle non-array values in file validation
1. [](#bugfix)
@@ -54,7 +87,7 @@
# v1.0.1
## 12/11/2015
1. [](#improved)
1. [](#improved)
* Reduced package sizes by removing extra vendor dev bits
1. [](#bugfix)
* Fix issue when you enable debugger from admin plugin
@@ -67,7 +100,7 @@
* Added setters to set state of CSS/JS pipelining
* Added `user/accounts` to `.gitignore`
* Added configurable permissions option for Image cache
1. [](#improved)
1. [](#improved)
* Hungarian translation updated
* Refactored Theme initialization for improved flexibility
* Wrapped security section of account blueprints in an 'super user' authorize check
@@ -101,7 +134,7 @@
* Automatically create unique security salt for each configuration
* Added Hungarian translation
* Added support for User groups
1. [](#improved)
1. [](#improved)
* Improved robots.txt to disallow crawling of non-user folders
* Nonces only generated once per action and process
* Added IP into Nonce string calculation

View File

@@ -23,12 +23,20 @@ The underlying architecture of Grav is designed to use well-established and _bes
# QuickStart
You have two options to get Grav:
These are the options to get Grav:
### Downloading a Grav Package
You can download a **ready-built** package from the [Downloads page on http://getgrav.org](http://getgrav.org/downloads)
### With composer
You can create a new project with the latest **stable** Grav release with the following command:
```
$ composer create-project getgrav/grav ~/webroot/grav
```
### From GitHub
1. Clone the Grav repository from [https://github.com/getgrav/grav]() to a folder in the webroot of your server, e.g. `~/webroot/grav`. Launch a **terminal** or **console** and navigate to the webroot folder:

View File

@@ -1,6 +1,6 @@
{
"name": "getgrav/grav",
"type": "library",
"type": "project",
"description": "Modern, Crazy Fast, Ridiculously Easy and Amazingly Powerful Flat-File CMS",
"keywords": ["cms","flat-file cms","flat cms","flatfile cms","php"],
"homepage": "http://getgrav.org",
@@ -8,7 +8,6 @@
"require": {
"php": ">=5.5.9",
"twig/twig": "~1.23",
"erusev/parsedown": "dev-master as 1.6.0",
"erusev/parsedown-extra": "~0.7",
"symfony/yaml": "~2.8",
"symfony/console": "~2.8",
@@ -44,5 +43,13 @@
},
"archive": {
"exclude": ["VERSION"]
},
"scripts": {
"post-create-project-cmd": "bin/grav install"
},
"extra": {
"branch-alias": {
"dev-develop": "1.x-dev"
}
}
}

View File

@@ -40,6 +40,8 @@ class Cache extends Getters
protected $driver_name;
protected $driver_setting;
/**
* @var bool
*/
@@ -108,13 +110,15 @@ class Cache extends Getters
// Cache key allows us to invalidate all cache on configuration changes.
$this->key = ($prefix ? $prefix : 'g') . '-' . substr(md5($uri->rootUrl(true) . $this->config->key() . GRAV_VERSION), 2, 8);
$this->driver_setting = $this->config->get('system.cache.driver');
$this->driver = $this->getCacheDriver();
// Set the cache namespace to our unique key
$this->driver->setNamespace($this->key);
// Dump Cache state
$grav['debugger']->addMessage('Cache: [' . ($this->enabled ? 'true' : 'false') . '] Driver: [' . $this->driver_name . ']');
$grav['debugger']->addMessage('Cache: [' . ($this->enabled ? 'true' : 'false') . '] Setting: [' . $this->driver_setting . '] Driver: [' . $this->driver_name . ']');
}
@@ -127,7 +131,7 @@ class Cache extends Getters
*/
public function getCacheDriver()
{
$setting = $this->config->get('system.cache.driver');
$setting = $this->driver_setting;
$driver_name = 'file';
if (!$setting || $setting == 'auto') {

View File

@@ -7,23 +7,26 @@ class Response
{
/**
* The callback for the progress
*
* @var callable Either a function or callback in array notation
*/
public static $callback = null;
/**
/**
* Which method to use for HTTP calls, can be 'curl', 'fopen' or 'auto'. Auto is default and fopen is the preferred method
*
* @var string
*/
private static $method = 'auto';
/**
* Default parameters for `curl` and `fopen`
*
* @var array
*/
private static $defaults = [
'curl' => [
'curl' => [
CURLOPT_REFERER => 'Grav GPM',
CURLOPT_USERAGENT => 'Grav GPM',
CURLOPT_RETURNTRANSFER => true,
@@ -51,7 +54,9 @@ class Response
/**
* Sets the preferred method to use for making HTTP calls.
*
* @param string $method Default is `auto`
*
* @return Response
*/
public static function setMethod($method = 'auto')
@@ -67,9 +72,11 @@ class Response
/**
* Makes a request to the URL by using the preferred method
* @param string $uri URL to call
* @param array $options An array of parameters for both `curl` and `fopen`
*
* @param string $uri URL to call
* @param array $options An array of parameters for both `curl` and `fopen`
* @param callable $callback Either a function or callback in array notation
*
* @return string The response of the request
*/
public static function get($uri = '', $options = [], $callback = null)
@@ -83,7 +90,8 @@ class Response
if (!Utils::isFunctionDisabled('set_time_limit') && !ini_get('safe_mode') && function_exists('set_time_limit')) {
set_time_limit(0);
}
} catch (\Exception $e) {}
} catch (\Exception $e) {
}
$options = array_replace_recursive(self::$defaults, $options);
$method = 'get' . ucfirst(strtolower(self::$method));
@@ -92,9 +100,31 @@ class Response
return static::$method($uri, $options, $callback);
}
/**
* Checks if cURL is available
*
* @return boolean
*/
public static function isCurlAvailable()
{
return function_exists('curl_version');
}
/**
* Checks if the remote fopen request is enabled in PHP
*
* @return boolean
*/
public static function isFopenAvailable()
{
return preg_match('/1|yes|on|true/i', ini_get('allow_url_fopen'));
}
/**
* Progress normalized for cURL and Fopen
*
* @param args Variable length of arguments passed in by stream method
*
* @return array Normalized array with useful data.
* Format: ['code' => int|false, 'filesize' => bytes, 'transferred' => bytes, 'percent' => int]
*/
@@ -115,7 +145,7 @@ class Response
}
if ($bytes_transferred > 0) {
if ($notification_code == STREAM_NOTIFY_PROGRESS|STREAM_NOTIFY_COMPLETED || $isCurlResource) {
if ($notification_code == STREAM_NOTIFY_PROGRESS | STREAM_NOTIFY_COMPLETED || $isCurlResource) {
$progress = [
'code' => $notification_code,
@@ -131,31 +161,14 @@ class Response
}
}
/**
* Checks if cURL is available
* @return boolean
*/
public static function isCurlAvailable()
{
return function_exists('curl_version');
}
/**
* Checks if the remote fopen request is enabled in PHP
* @return boolean
*/
public static function isFopenAvailable()
{
return preg_match('/1|yes|on|true/i', ini_get('allow_url_fopen'));
}
/**
* Automatically picks the preferred method
*
* @return string The response of the request
*/
private static function getAuto()
{
if (self::isFopenAvailable()) {
if (!ini_get('open_basedir') && self::isFopenAvailable()) {
return self::getFopen(func_get_args());
}
@@ -164,46 +177,9 @@ class Response
}
}
/**
* Starts a HTTP request via cURL
* @return string The response of the request
*/
private static function getCurl()
{
$args = func_get_args();
$args = count($args) > 1 ? $args : array_shift($args);
$uri = $args[0];
$options = $args[1];
$callback = $args[2];
$ch = curl_init($uri);
curl_setopt_array($ch, $options['curl']);
if ($callback) {
curl_setopt_array(
$ch,
[
CURLOPT_NOPROGRESS => false,
CURLOPT_PROGRESSFUNCTION => ['self', 'progress']
]
);
}
$response = curl_exec($ch);
if ($errno = curl_errno($ch)) {
$error_message = curl_strerror($errno);
throw new \RuntimeException("cURL error ({$errno}):\n {$error_message}");
}
curl_close($ch);
return $response;
}
/**
* Starts a HTTP request via fopen
*
* @return string The response of the request
*/
private static function getFopen()
@@ -229,4 +205,99 @@ class Response
return $content;
}
/**
* Starts a HTTP request via cURL
*
* @return string The response of the request
*/
private static function getCurl()
{
$args = func_get_args();
$args = count($args) > 1 ? $args : array_shift($args);
$uri = $args[0];
$options = $args[1];
$callback = $args[2];
$ch = curl_init($uri);
$response = static::_curl_exec_follow($ch, $options, $callback);
if ($errno = curl_errno($ch)) {
$error_message = curl_strerror($errno);
throw new \RuntimeException("cURL error ({$errno}):\n {$error_message}");
}
curl_close($ch);
return $response;
}
private static function _curl_exec_follow($ch, $options, $callback)
{
if ($callback) {
curl_setopt_array(
$ch,
[
CURLOPT_NOPROGRESS => false,
CURLOPT_PROGRESSFUNCTION => ['self', 'progress']
]
);
}
// no open_basedir set, we can proceed normally
if (!ini_get('open_basedir')) {
curl_setopt_array($ch, $options['curl']);
return curl_exec($ch);
}
$max_redirects = isset($options['curl'][CURLOPT_MAXREDIRS]) ? $options['curl'][CURLOPT_MAXREDIRS] : 3;
$options['curl'][CURLOPT_FOLLOWLOCATION] = false;
// open_basedir set but no redirects to follow, we can disable followlocation and proceed normally
curl_setopt_array($ch, $options['curl']);
if ($max_redirects <= 0) {
return curl_exec($ch);
}
$uri = curl_getinfo($ch, CURLINFO_EFFECTIVE_URL);
$rch = curl_copy_handle($ch);
curl_setopt($rch, CURLOPT_HEADER, true);
curl_setopt($rch, CURLOPT_NOBODY, true);
curl_setopt($rch, CURLOPT_FORBID_REUSE, false);
curl_setopt($rch, CURLOPT_RETURNTRANSFER, true);
do {
curl_setopt($rch, CURLOPT_URL, $uri);
$header = curl_exec($rch);
if (curl_errno($rch)) {
$code = 0;
} else {
$code = curl_getinfo($rch, CURLINFO_HTTP_CODE);
if ($code == 301 || $code == 302) {
preg_match('/Location:(.*?)\n/', $header, $matches);
$uri = trim(array_pop($matches));
} else {
$code = 0;
}
}
} while ($code && --$max_redirects);
curl_close($rch);
if (!$max_redirects) {
if ($max_redirects === null) {
trigger_error('Too many redirects. When following redirects, libcurl hit the maximum amount.', E_USER_WARNING);
}
return false;
}
curl_setopt($ch, CURLOPT_URL, $uri);
return curl_exec($ch);
}
}

View File

@@ -209,9 +209,9 @@ trait ParsedownGravTrait
}
// if there is a media file that matches the path referenced..
if ($media && isset($media->all()[$url['path']])) {
if ($media && isset($media->all()[$path_parts['basename']])) {
// get the medium object
$medium = $media->all()[$url['path']];
$medium = $media->all()[$path_parts['basename']];
// if there is a query, then parse it and build action calls
if (isset($url['query'])) {

View File

@@ -453,6 +453,36 @@ abstract class Utils
* @return string the nonce string
*/
private static function generateNonceString($action, $plusOneTick = false)
{
if (!empty($_SERVER['HTTP_CLIENT_IP'])) {
$ip = $_SERVER['HTTP_CLIENT_IP'];
} elseif (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
$ip = $_SERVER['HTTP_X_FORWARDED_FOR'];
} else {
$ip = $_SERVER['REMOTE_ADDR'];
}
$username = '';
if (isset(self::getGrav()['user'])) {
$user = self::getGrav()['user'];
$username = $user->username;
}
$username .= $ip;
$token = session_id();
$i = self::nonceTick();
if ($plusOneTick) {
$i++;
}
return ( $i . '|' . $action . '|' . $username . '|' . $token . '|' . self::getGrav()['config']->get('security.salt'));
}
//Added in version 1.0.8 to ensure that existing nonces are not broken.
//TODO: to be removed
private static function generateNonceStringOldStyle($action, $plusOneTick = false)
{
if (isset(self::getGrav()['user'])) {
$user = self::getGrav()['user'];
@@ -463,14 +493,11 @@ abstract class Utils
} else {
$username = isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : '';
}
$token = session_id();
$i = self::nonceTick();
if ($plusOneTick) {
$i++;
}
return ( $i . '|' . $action . '|' . $username . '|' . $token . '|' . self::getGrav()['config']->get('security.salt'));
}
@@ -509,6 +536,20 @@ abstract class Utils
return static::$nonces[$action];
}
//Added in version 1.0.8 to ensure that existing nonces are not broken.
//TODO: to be removed
public static function getNonceOldStyle($action, $plusOneTick = false)
{
// Don't regenerate this again if not needed
if (isset(static::$nonces[$action])) {
return static::$nonces[$action];
}
$nonce = md5(self::generateNonceStringOldStyle($action, $plusOneTick));
static::$nonces[$action] = $nonce;
return static::$nonces[$action];
}
/**
* Verify the passed nonce for the give action
*
@@ -530,6 +571,21 @@ abstract class Utils
return true;
}
//Added in version 1.0.8 to ensure that existing nonces are not broken.
//TODO: to be removed
//Nonce generated 0-12 hours ago
if ($nonce == self::getNonceOldStyle($action)) {
return true;
}
//Nonce generated 12-24 hours ago
$plusOneTick = true;
if ($nonce == self::getNonceOldStyle($action, $plusOneTick)) {
return true;
}
//End TODO: to be removed
//Invalid nonce
return false;
}