Merge branch 'develop' into feature/introduce-user-groups

This commit is contained in:
Flavio Copes
2015-11-04 14:54:56 +01:00
27 changed files with 357 additions and 212 deletions

View File

@@ -44,15 +44,17 @@ RewriteRule .* index.php [L]
## Begin - Security
# Block all direct access for these folders
RewriteRule ^(.git|cache|bin|logs|backup)/(.*) error [L]
# Block access to specific file types for these folders
RewriteRule ^(system|user|vendor)/(.*)\.(txt|md|html|yaml|php|twig|sh|bat)$ error [L]
RewriteRule ^(.git|cache|bin|logs|backup)/(.*) error [F]
# Block access to specific file types for these system folders
RewriteRule ^(system|vendor)/(.*)\.(txt|xml|md|html|yaml|php|pl|py|cgi|twig|sh|bat)$ error [F]
# Block access to specific file types for these user folders
RewriteRule ^(user)/(.*)\.(txt|md|yaml|php|pl|py|cgi|twig|sh|bat)$ error [F]
# Block all direct access to .md files:
RewriteRule \.md$ error [L]
RewriteRule \.md$ error [F]
# Block all direct access to files and folders beginning with a dot
RewriteRule (^\.|/\.) - [F]
# Block access to specific files in the root folder
RewriteRule ^(LICENSE|composer.lock|composer.json|nginx.conf|web.config)$ error [F]
RewriteRule ^(LICENSE|composer.lock|composer.json|nginx.conf|web.config|htaccess.txt|\.htaccess)$ error [F]
## End - Security
</IfModule>

View File

@@ -1,9 +1,30 @@
# v1.0.0-rc.4
## 10/29/2015
1. [](#bugfix)
* Fixed a fatal error if you have a collection with missing or invalid `@page: /route`
# v1.0.0-rc.3
## 10/29/2015
1. [](#new)
* New Page collection options! `@self.parent, @self.siblings, @self.descendants` + more
* Whitelist of file types for fallback route functionality (images by default)
1. [](#improved)
* Assets switched from defines to streams
1. [](#bugfix)
* README.md typos fixed
* Fixed issue with routes that have lang string in them (`/en/english`)
* Trim strings before validation so whitespace is not satisfy 'required'
# v1.0.0-rc.2
## xx/xx/2015
## 10/27/2015
1. [](#new)
* Added support for CSS Asset groups
* Added a `wrapped_site` system option for themes/plugins to use
* Pass `Page` object as event to `onTwigPageVariables()` event hook
* New `Data.items()` method to get all items
1. [](#improved)
* Missing pipelined remote asset will now fail quietly
* More reliably handle inline JS and CSS to remove only surrounding HTML tags
@@ -11,8 +32,10 @@
* Improved Medium metadata merging to allow for automatic title/alt/class attributes
* Moved Grav object to global variable rather than template variable (useful for macros)
* German language improvements
* Updated bundled composer
1. [](#bugfix)
* Accept variety of `true` values in `User.authorize()` method
* Fix for `Validation` throwing an error if no label set
# v1.0.0-rc.1
## 10/23/2015

View File

@@ -9,10 +9,10 @@ The underlying architecture of Grav is designed to use well-established and _bes
* [Twig Templating](http://twig.sensiolabs.org/): for powerful control of the user interface
* [Markdown](http://en.wikipedia.org/wiki/Markdown): for easy content creation
* [YAML](http://yaml.org): for simple configuration
* [Parsedown](http://parsedown.org/): for fast Markdown and Mardown Extra support
* [Parsedown](http://parsedown.org/): for fast Markdown and Markdown Extra support
* [Doctrine Cache](http://docs.doctrine-project.org/en/2.0.x/reference/caching.html): layer for performance
* [Pimple Dependency Injection Container](http://pimple.sensiolabs.org/): for extensibility and maintainability
* [Symfony Event Dispacher](http://symfony.com/doc/current/components/event_dispatcher/introduction.html): for plugin event handling
* [Symfony Event Dispatcher](http://symfony.com/doc/current/components/event_dispatcher/introduction.html): for plugin event handling
* [Symfony Console](http://symfony.com/doc/current/components/console/introduction.html): for CLI interface
* [Gregwar Image Library](https://github.com/Gregwar/Image): for dynamic image manipulation
@@ -53,7 +53,7 @@ You can download [plugins](http://getgrav.org/downloads/plugins) or [themes](htt
$ bin/gpm index
```
This will display all the available plugins and then you can install one ore more with:
This will display all the available plugins and then you can install one or more with:
```
$ bin/gpm install <plugin/theme>
@@ -76,7 +76,7 @@ $ bin/gpm update
# Contributing
We appreciate any contribution to Grav, whether it is related to bugs, grammar, or simply a suggestion or improvement.
However, we ask that any contribution follow our simple guidelines in order to be properly received.
However, we ask that any contributions follow our simple guidelines in order to be properly received.
All our projects follow the [GitFlow branching model][gitflow-model], from development to release. If you are not familiar with it, there are several guides and tutorials to make you understand what it is about.

74
composer.lock generated
View File

@@ -8,16 +8,16 @@
"packages": [
{
"name": "doctrine/cache",
"version": "v1.4.2",
"version": "v1.5.0",
"source": {
"type": "git",
"url": "https://github.com/doctrine/cache.git",
"reference": "8c434000f420ade76a07c64cbe08ca47e5c101ca"
"reference": "eb8a73619af4f1c8711e2ce482f5de3643258a1f"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/doctrine/cache/zipball/8c434000f420ade76a07c64cbe08ca47e5c101ca",
"reference": "8c434000f420ade76a07c64cbe08ca47e5c101ca",
"url": "https://api.github.com/repos/doctrine/cache/zipball/eb8a73619af4f1c8711e2ce482f5de3643258a1f",
"reference": "eb8a73619af4f1c8711e2ce482f5de3643258a1f",
"shasum": ""
},
"require": {
@@ -38,8 +38,8 @@
}
},
"autoload": {
"psr-0": {
"Doctrine\\Common\\Cache\\": "lib/"
"psr-4": {
"Doctrine\\Common\\Cache\\": "lib/Doctrine/Common/Cache"
}
},
"notification-url": "https://packagist.org/downloads/",
@@ -74,7 +74,7 @@
"cache",
"caching"
],
"time": "2015-08-31 12:36:41"
"time": "2015-10-28 11:27:45"
},
{
"name": "donatj/phpuseragentparser",
@@ -714,16 +714,16 @@
},
{
"name": "symfony/console",
"version": "v2.7.5",
"version": "v2.7.6",
"source": {
"type": "git",
"url": "https://github.com/symfony/console.git",
"reference": "06cb17c013a82f94a3d840682b49425cd00a2161"
"reference": "5efd632294c8320ea52492db22292ff853a43766"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/console/zipball/06cb17c013a82f94a3d840682b49425cd00a2161",
"reference": "06cb17c013a82f94a3d840682b49425cd00a2161",
"url": "https://api.github.com/repos/symfony/console/zipball/5efd632294c8320ea52492db22292ff853a43766",
"reference": "5efd632294c8320ea52492db22292ff853a43766",
"shasum": ""
},
"require": {
@@ -732,7 +732,6 @@
"require-dev": {
"psr/log": "~1.0",
"symfony/event-dispatcher": "~2.1",
"symfony/phpunit-bridge": "~2.7",
"symfony/process": "~2.1"
},
"suggest": {
@@ -767,20 +766,20 @@
],
"description": "Symfony Console Component",
"homepage": "https://symfony.com",
"time": "2015-09-25 08:32:23"
"time": "2015-10-20 14:38:46"
},
{
"name": "symfony/event-dispatcher",
"version": "v2.7.5",
"version": "v2.7.6",
"source": {
"type": "git",
"url": "https://github.com/symfony/event-dispatcher.git",
"reference": "ae4dcc2a8d3de98bd794167a3ccda1311597c5d9"
"reference": "87a5db5ea887763fa3a31a5471b512ff1596d9b8"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/ae4dcc2a8d3de98bd794167a3ccda1311597c5d9",
"reference": "ae4dcc2a8d3de98bd794167a3ccda1311597c5d9",
"url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/87a5db5ea887763fa3a31a5471b512ff1596d9b8",
"reference": "87a5db5ea887763fa3a31a5471b512ff1596d9b8",
"shasum": ""
},
"require": {
@@ -791,7 +790,6 @@
"symfony/config": "~2.0,>=2.0.5",
"symfony/dependency-injection": "~2.6",
"symfony/expression-language": "~2.6",
"symfony/phpunit-bridge": "~2.7",
"symfony/stopwatch": "~2.3"
},
"suggest": {
@@ -825,28 +823,25 @@
],
"description": "Symfony EventDispatcher Component",
"homepage": "https://symfony.com",
"time": "2015-09-22 13:49:29"
"time": "2015-10-11 09:39:48"
},
{
"name": "symfony/var-dumper",
"version": "v2.7.5",
"version": "v2.7.6",
"source": {
"type": "git",
"url": "https://github.com/symfony/var-dumper.git",
"reference": "ba8c9a0edf18f70a7efcb8d3eb35323a10263338"
"reference": "eb033050050916b6bfa51be71009ef67b16046c9"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/var-dumper/zipball/ba8c9a0edf18f70a7efcb8d3eb35323a10263338",
"reference": "ba8c9a0edf18f70a7efcb8d3eb35323a10263338",
"url": "https://api.github.com/repos/symfony/var-dumper/zipball/eb033050050916b6bfa51be71009ef67b16046c9",
"reference": "eb033050050916b6bfa51be71009ef67b16046c9",
"shasum": ""
},
"require": {
"php": ">=5.3.9"
},
"require-dev": {
"symfony/phpunit-bridge": "~2.7"
},
"suggest": {
"ext-symfony_debug": ""
},
@@ -884,28 +879,25 @@
"debug",
"dump"
],
"time": "2015-09-22 14:41:01"
"time": "2015-10-25 17:17:38"
},
{
"name": "symfony/yaml",
"version": "v2.7.5",
"version": "v2.7.6",
"source": {
"type": "git",
"url": "https://github.com/symfony/yaml.git",
"reference": "31cb2ad0155c95b88ee55fe12bc7ff92232c1770"
"reference": "eca9019c88fbe250164affd107bc8057771f3f4d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/yaml/zipball/31cb2ad0155c95b88ee55fe12bc7ff92232c1770",
"reference": "31cb2ad0155c95b88ee55fe12bc7ff92232c1770",
"url": "https://api.github.com/repos/symfony/yaml/zipball/eca9019c88fbe250164affd107bc8057771f3f4d",
"reference": "eca9019c88fbe250164affd107bc8057771f3f4d",
"shasum": ""
},
"require": {
"php": ">=5.3.9"
},
"require-dev": {
"symfony/phpunit-bridge": "~2.7"
},
"type": "library",
"extra": {
"branch-alias": {
@@ -933,20 +925,20 @@
],
"description": "Symfony Yaml Component",
"homepage": "https://symfony.com",
"time": "2015-09-14 14:14:09"
"time": "2015-10-11 09:39:48"
},
{
"name": "twig/twig",
"version": "v1.22.3",
"version": "v1.23.0",
"source": {
"type": "git",
"url": "https://github.com/twigphp/Twig.git",
"reference": "ebfc36b7e77b0c1175afe30459cf943010245540"
"reference": "5868cd822fd6cf626d5f805439575f9c323cee2a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/twigphp/Twig/zipball/ebfc36b7e77b0c1175afe30459cf943010245540",
"reference": "ebfc36b7e77b0c1175afe30459cf943010245540",
"url": "https://api.github.com/repos/twigphp/Twig/zipball/5868cd822fd6cf626d5f805439575f9c323cee2a",
"reference": "5868cd822fd6cf626d5f805439575f9c323cee2a",
"shasum": ""
},
"require": {
@@ -959,7 +951,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.22-dev"
"dev-master": "1.23-dev"
}
},
"autoload": {
@@ -994,7 +986,7 @@
"keywords": [
"templating"
],
"time": "2015-10-13 07:07:02"
"time": "2015-10-29 23:29:01"
}
],
"packages-dev": [],

View File

@@ -44,15 +44,17 @@ RewriteRule .* index.php [L]
## Begin - Security
# Block all direct access for these folders
RewriteRule ^(.git|cache|bin|logs|backup)/(.*) error [L]
# Block access to specific file types for these folders
RewriteRule ^(system|user|vendor)/(.*)\.(txt|md|html|yaml|php|twig|sh|bat)$ error [L]
RewriteRule ^(.git|cache|bin|logs|backup)/(.*) error [F]
# Block access to specific file types for these system folders
RewriteRule ^(system|vendor)/(.*)\.(txt|xml|md|html|yaml|php|pl|py|cgi|twig|sh|bat)$ error [F]
# Block access to specific file types for these user folders
RewriteRule ^(user)/(.*)\.(txt|md|yaml|php|pl|py|cgi|twig|sh|bat)$ error [F]
# Block all direct access to .md files:
RewriteRule \.md$ error [L]
RewriteRule \.md$ error [F]
# Block all direct access to files and folders beginning with a dot
RewriteRule (^\.|/\.) - [F]
# Block access to specific files in the root folder
RewriteRule ^(LICENSE|composer.lock|composer.json|nginx.conf|web.config)$ error [F]
RewriteRule ^(LICENSE|composer.lock|composer.json|nginx.conf|web.config|htaccess.txt|\.htaccess)$ error [F]
## End - Security
</IfModule>

View File

@@ -1,87 +1,44 @@
worker_processes 1;
server {
#listen 80;
index index.html index.php;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;
server {
listen 80;
server_name localhost;
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
location / {
root html;
index index.php;
if (!-e $request_filename){ rewrite ^(.*)$ /index.php last; }
}
# if you want grav in a sub-directory of your main site
# (for example, example.com/mygrav) then you need this rewrite:
location /mygrav {
index index.php;
if (!-e $request_filename){ rewrite ^(.*)$ /mygrav/$2 last; }
try_files $uri $uri/ /index.php?$args;
}
# if using grav in a sub-directory of your site,
# prepend the actual path to each location
# for example: /mygrav/images
# and: /mygrav/user
# and: /mygrav/cache
# and so on
location /images/ {
# Serve images as static
}
location /user {
rewrite ^/user/accounts/(.*)$ /error redirect;
rewrite ^/user/config/(.*)$ /error redirect;
rewrite ^/user/(.*)\.(txt|md|html|php|yaml|json|twig|sh|bat)$ /error redirect;
}
location /cache {
rewrite ^/cache/(.*) /error redirect;
}
location /bin {
rewrite ^/bin/(.*)$ /error redirect;
}
location /backup {
rewrite ^/backup/(.*) /error redirect;
}
location /system {
rewrite ^/system/(.*)\.(txt|md|html|php|yaml|json|twig|sh|bat)$ /error redirect;
}
location /vendor {
rewrite ^/vendor/(.*)\.(txt|md|html|php|yaml|json|twig|sh|bat)$ /error redirect;
}
# Remember to change 127.0.0.1:9000 to the Ip/port
# you configured php-cgi.exe to run from
location ~ \.php$ {
try_files $uri =404;
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
## Begin - Server Info
root /home/user/www/html;
server_name localhost;
## End - Server Info
## Begin - Index
# for subfolders, simply adjust:
# `location /subfolder {`
# and the rewrite to use `/subfolder/index.php`
location / {
try_files $uri $uri/ /index.html;
if (!-e $request_filename){ rewrite ^(.*)$ /index.php last; }
}
## End - Index
## Begin - PHP
location ~ \.php$ {
# Choose either a socket or TCP/IP address
fastcgi_pass unix:/var/run/php5-fpm.sock;
# fastcgi_pass 127.0.0.1:9000;
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_index index.php;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root/$fastcgi_script_name;
}
## End - PHP
## Begin - Security
# deny all direct access for these folders
location ~* /(.git|cache|bin|logs|backups)/.*$ { return 403; }
# deny running scripts inside core system folders
location ~* /(system|vendor)/.*\.(txt|xml|md|html|yaml|php|pl|py|cgi|twig|sh|bat)$ { return 403; }
# deny running scripts inside user folder
location ~* /user/.*\.(txt|md|yaml|php|pl|py|cgi|twig|sh|bat)$ { return 403; }
# deny access to specific files in the root folder
location ~ /(LICENSE|composer.lock|composer.json|nginx.conf|web.config|htaccess.txt|\.htaccess) { return 403; }
## End - Security
}

View File

@@ -215,6 +215,15 @@ form:
validate:
type: bool
pages.fallback_types:
type: selectize
size: large
label: PLUGIN_ADMIN.FALLBACK_TYPES
help: PLUGIN_ADMIN.FALLBACK_TYPES_HELP
classes: fancy
validate:
type: commalist
languages:
type: section
title: PLUGIN_ADMIN.LANGUAGES
@@ -774,6 +783,7 @@ form:
param_sep:
type: select
size: medium
label: PLUGIN_ADMIN.PARAMETER_SEPARATOR
classes: fancy
help: PLUGIN_ADMIN.PARAMETER_SEPARATOR_HELP

View File

@@ -29,7 +29,7 @@ form:
size: large
label: PLUGIN_ADMIN.PASSWORD
validate:
required: true
required: false
message: PLUGIN_ADMIN.PASSWORD_VALIDATION_MESSAGE
pattern: '(?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{8,}'

View File

@@ -1,9 +1,4 @@
schemes:
asset:
type: ReadOnlyStream
paths:
- assets
image:
type: ReadOnlyStream
paths:

View File

@@ -49,11 +49,12 @@ pages:
vary_accept_encoding: false # Add `Vary: Accept-Encoding` header
redirect_default_route: false # Automatically redirect to a page's default route
redirect_default_code: 301 # Default code to use for redirects
redirect_trailing_slash: true # Handle automatically or 301 redirect a trailing / URL
redirect_trailing_slash: true # Handle automatically or 301 redirect a trailing / URL
ignore_files: [.DS_Store] # Files to ignore in Pages
ignore_folders: [.git, .idea] # Folders to ignore in Pages
ignore_hidden: true # Ignore all Hidden files and folders
url_taxonomy_filters: true # Enable auto-magic URL-based taxonomy filters for page collections
fallback_types: [png,jpg,jpeg,gif] # Allowed types of files found if accessed via Page route
cache:
enabled: true # Set to true to enable caching
@@ -71,6 +72,7 @@ twig:
autoescape: false # Autoescape Twig vars
undefined_functions: true # Allow undefined functions
undefined_filters: true # Allow undefined filters
umask_fix: false # By default Twig creates cached files as 755, fix switches this to 775
assets: # Configuration for Assets Manager (JS, CSS)
css_pipeline: false # The CSS pipeline is the unification of multiple CSS resources into one file

View File

@@ -2,7 +2,7 @@
// Some standard defines
define('GRAV', true);
define('GRAV_VERSION', '1.0.0-rc.1');
define('GRAV_VERSION', '1.0.0-rc.4');
define('DS', '/');
// Directories and Paths
@@ -13,14 +13,14 @@ define('ROOT_DIR', GRAV_ROOT . '/');
define('USER_PATH', 'user/');
define('USER_DIR', ROOT_DIR . USER_PATH);
define('SYSTEM_DIR', ROOT_DIR .'system/');
define('ASSETS_DIR', ROOT_DIR . 'assets/');
define('CACHE_DIR', ROOT_DIR . 'cache/');
define('IMAGES_DIR', ROOT_DIR . 'images/');
define('LOG_DIR', ROOT_DIR .'logs/');
define('ACCOUNTS_DIR', USER_DIR .'accounts/');
define('PAGES_DIR', USER_DIR .'pages/');
// DEPRECATED: Do not use!
define('ASSETS_DIR', ROOT_DIR . 'assets/');
define('IMAGES_DIR', ROOT_DIR . 'images/');
define('ACCOUNTS_DIR', USER_DIR .'accounts/');
define('PAGES_DIR', USER_DIR .'pages/');
define('DATA_DIR', USER_DIR .'data/');
define('LIB_DIR', SYSTEM_DIR .'src/');
define('PLUGINS_DIR', USER_DIR .'plugins/');

View File

@@ -74,6 +74,8 @@ class Assets
protected $config;
protected $base_url;
protected $timestamp = '';
protected $assets_dir;
protected $assets_url;
// Default values for pipeline settings
protected $css_minify = true;
@@ -117,7 +119,7 @@ class Assets
}
// Pipeline requires public dir
if (($this->js_pipeline || $this->css_pipeline) && !is_dir(ASSETS_DIR)) {
if (($this->js_pipeline || $this->css_pipeline) && !is_dir($this->assets_dir)) {
throw new \Exception('Assets: Public dir not found');
}
@@ -175,6 +177,11 @@ class Assets
$base_url = self::getGrav()['base_url'];
$asset_config = (array)$config->get('system.assets');
/** @var Locator $locator */
$locator = self::$grav['locator'];
$this->assets_dir = self::getGrav()['locator']->findResource('asset://') . DS;
$this->assets_url = self::getGrav()['locator']->findResource('asset://', false);
$this->config($asset_config);
$this->base_url = $base_url . '/';
@@ -621,8 +628,8 @@ class Assets
$file = md5(json_encode($this->css) . $this->css_minify . $this->css_rewrite . $group) . '.css';
$relative_path = "{$this->base_url}" . basename(ASSETS_DIR) . "/{$file}";
$absolute_path = ASSETS_DIR . $file;
$relative_path = "{$this->base_url}{$this->assets_url}/{$file}";
$absolute_path = $this->assets_dir . $file;
// If pipeline exist return it
if (file_exists($absolute_path)) {
@@ -689,8 +696,8 @@ class Assets
$file = md5(json_encode($this->js) . $this->js_minify . $group) . '.js';
$relative_path = "{$this->base_url}" . basename(ASSETS_DIR) . "/{$file}";
$absolute_path = ASSETS_DIR . $file;
$relative_path = "{$this->base_url}{$this->assets_url}/{$file}";
$absolute_path = $this->assets_dir . $file;
// If pipeline exist return it
if (file_exists($absolute_path)) {
@@ -852,12 +859,12 @@ class Assets
public function addDir($directory, $pattern = self::DEFAULT_REGEX)
{
// Check if public_dir exists
if (!is_dir(ASSETS_DIR)) {
if (!is_dir($this->assets_dir)) {
throw new Exception('Assets: Public dir not found');
}
// Get files
$files = $this->rglob(ASSETS_DIR . DIRECTORY_SEPARATOR . $directory, $pattern, ASSETS_DIR);
$files = $this->rglob($this->assets_dir . DIRECTORY_SEPARATOR . $directory, $pattern, $this->assets_dir);
// No luck? Nothing to do
if (!$files) {

View File

@@ -21,6 +21,8 @@ use Grav\Common\Filesystem\Folder;
*/
class Cache extends Getters
{
use GravTrait;
/**
* @var string Cache key.
*/
@@ -44,30 +46,30 @@ class Cache extends Getters
protected $cache_dir;
protected static $standard_remove = [
'cache/twig/',
'cache/doctrine/',
'cache/compiled/',
'cache/validated-',
'images/',
'assets/',
'cache://twig/',
'cache://doctrine/',
'cache://compiled/',
'cache://validated-',
'cache://images',
'asset://',
];
protected static $all_remove = [
'cache/',
'images/',
'assets/'
'cache://',
'cache://images',
'asset://'
];
protected static $assets_remove = [
'assets/'
'asset://'
];
protected static $images_remove = [
'images/'
'cache://images'
];
protected static $cache_remove = [
'cache/'
'cache://'
];
/**
@@ -221,7 +223,7 @@ class Cache extends Getters
*/
public static function clearCache($remove = 'standard')
{
$locator = self::getGrav()['locator'];
$output = [];
$user_config = USER_DIR . 'config/system.yaml';
@@ -243,10 +245,16 @@ class Cache extends Getters
}
foreach ($remove_paths as $path) {
foreach ($remove_paths as $stream) {
// Convert stream to a real path
$path = $locator->findResource($stream, true, true);
// Make sure path exists before proceeding, otherwise we would wipe ROOT_DIR
if (!$path)
throw new \RuntimeException("Stream '{$stream}' not found", 500);
$anything = false;
$files = glob(ROOT_DIR . $path . '*');
$files = glob($path . '/*');
if (is_array($files)) {
foreach ($files as $file) {
@@ -263,7 +271,7 @@ class Cache extends Getters
}
if ($anything) {
$output[] = '<red>Cleared: </red>' . $path . '*';
$output[] = '<red>Cleared: </red>' . $path . '/*';
}
}

View File

@@ -30,6 +30,12 @@ class Config extends Data
'' => ['user'],
]
],
'asset' => [
'type' => 'ReadOnlyStream',
'prefixes' => [
'' => ['assets'],
]
],
'blueprints' => [
'type' => 'ReadOnlyStream',
'prefixes' => [
@@ -194,7 +200,7 @@ class Config extends Data
$checkConfig = $this->get('system.cache.check.config', true);
$checkSystem = $this->get('system.cache.check.system', true);
if (!$checkBlueprints && !!$checkLanguages && $checkConfig && !$checkSystem) {
if (!$checkBlueprints && !$checkLanguages && !$checkConfig && !$checkSystem) {
$this->messages[] = 'Skip configuration timestamp check.';
return false;
}

View File

@@ -76,7 +76,8 @@ class Blueprint
try {
$this->validateArray($data, $this->nested);
} catch (\RuntimeException $e) {
throw new \RuntimeException(sprintf('<b>Validation failed:</b> %s', $e->getMessage()));
$language = self::getGrav()['language'];
throw new \RuntimeException(sprintf('<b>Validation failed:</b> %s', $language->translate($e->getMessage())));
}
}

View File

@@ -224,16 +224,6 @@ class Data implements DataInterface
return $this->file()->raw();
}
/**
* Return the data items.
*
* @return array
*/
public function items()
{
return $this->items;
}
/**
* Set or get the data storage.
*

View File

@@ -588,6 +588,10 @@ class Validation
public static function validateRequired($value, $params)
{
if (is_string($value)) {
$value = trim($value);
}
return (bool) $params !== true || !empty($value);
}

View File

@@ -114,7 +114,7 @@ class Grav extends Container
/** @var Uri $uri */
$uri = $c['uri'];
$path = rtrim($uri->path(), '/');
$path = $uri->path(); // Don't trim to support trailing slash default routes
$path = $path ?: '/';
$page = $pages->dispatch($path);
@@ -296,7 +296,10 @@ class Grav extends Container
if ($uri->isExternal($route)) {
$url = $route;
} else {
$url = rtrim($uri->rootUrl(), '/') .'/'. trim($route, '/');
if ($this['config']->get('system.pages.redirect_trailing_slash', true))
$url = rtrim($uri->rootUrl(), '/') .'/'. trim($route, '/'); // Remove trailing slash
else
$url = rtrim($uri->rootUrl(), '/') .'/'. ltrim($route, '/'); // Support trailing slash default routes
}
header("Location: {$url}", true, $code);
@@ -457,6 +460,16 @@ class Grav extends Container
/** @var Uri $uri */
$uri = $this['uri'];
/** @var Config $config */
$config = $this['config'];
$uri_extension = $uri->extension();
// Only allow whitelisted types to fallback
if (!in_array($uri_extension, $config->get('system.pages.fallback_types'))) {
return;
}
$path_parts = pathinfo($path);
$page = $this['pages']->dispatch($path_parts['dirname'], true);
if ($page) {
@@ -478,7 +491,6 @@ class Grav extends Container
}
// unsupported media type, try to download it...
$uri_extension = $uri->extension();
if ($uri_extension) {
$extension = $uri_extension;
} else {
@@ -491,7 +503,7 @@ class Grav extends Container
if ($extension) {
$download = true;
if (in_array(ltrim($extension, '.'), $this['config']->get('system.media.unsupported_inline_types', []))) {
if (in_array(ltrim($extension, '.'), $config->get('system.media.unsupported_inline_types', []))) {
$download = false;
}
Utils::download($page->path() . DIRECTORY_SEPARATOR . $uri->basename(), $download);

View File

@@ -162,7 +162,7 @@ class Language
*/
public function setActiveFromUri($uri)
{
$regex = '/(^\/(' . $this->getAvailable() . ')).*/';
$regex = '/(^\/(' . $this->getAvailable() . '))(?:\/.*|$)/i';
// if languages set
if ($this->enabled()) {

View File

@@ -36,6 +36,18 @@ class Collection extends Iterator
return $this->params;
}
/**
* Add a single page to a collection
*
* @param Page $page
* @return $this
*/
public function addPage(Page $page)
{
$this->items[$page->path()] = ['slug' => $page->slug()];
return $this;
}
/**
*
* Create a copy of this collection
@@ -96,6 +108,7 @@ class Collection extends Iterator
* Remove item from the list.
*
* @param Page|string|null $key
* @return $this|void
* @throws \InvalidArgumentException
*/
public function remove($key = null)
@@ -110,6 +123,7 @@ class Collection extends Iterator
}
parent::remove($key);
return $this;
}
/**

View File

@@ -9,6 +9,14 @@ class ImageFile extends \Gregwar\Image\Image
{
use GravTrait;
/**
* Clear previously applied operations
*/
public function clearOperations()
{
$this->operations = [];
}
/**
* This is the same as the Gregwar Image class except this one fires a Grav Event on creation of new cached file
*

View File

@@ -260,6 +260,7 @@ class ImageMedium extends Medium
if ($this->image) {
$this->image();
$this->image->clearOperations(); // Clear previously applied operations
$this->filter();
}
@@ -359,6 +360,48 @@ class ImageMedium extends Medium
return empty($this->sizes) ? '100vw' : $this->sizes;
}
/**
* Allows to set the width attribute from Markdown or Twig
* Examples: ![Example](myimg.png?width=200&height=400)
* ![Example](myimg.png?resize=100,200&width=100&height=200)
* ![Example](myimg.png?width=auto&height=auto)
* ![Example](myimg.png?width&height)
* {{ page.media['myimg.png'].width().height().html }}
* {{ page.media['myimg.png'].resize(100,200).width(100).height(200).html }}
*
* @param mixed $value A value or 'auto' or empty to use the width of the image
* @return $this
*/
public function width($value = 'auto')
{
if (!$value || $value == 'auto')
$this->attributes['width'] = $this->get('width');
else
$this->attributes['width'] = $value;
return $this;
}
/**
* Allows to set the height attribute from Markdown or Twig
* Examples: ![Example](myimg.png?width=200&height=400)
* ![Example](myimg.png?resize=100,200&width=100&height=200)
* ![Example](myimg.png?width=auto&height=auto)
* ![Example](myimg.png?width&height)
* {{ page.media['myimg.png'].width().height().html }}
* {{ page.media['myimg.png'].resize(100,200).width(100).height(200).html }}
*
* @param mixed $value A value or 'auto' or empty to use the height of the image
* @return $this
*/
public function height($value = 'auto')
{
if (!$value || $value == 'auto')
$this->attributes['height'] = $this->get('height');
else
$this->attributes['height'] = $value;
return $this;
}
/**
* Forward the call to the image processing method.
*

View File

@@ -209,9 +209,13 @@ class Medium extends Data implements RenderableInterface
$style = '';
foreach ($this->styleAttributes as $key => $value) {
$style .= $key . ': ' . $value . ';';
if (is_numeric($key)) // Special case for inline style attributes, refer to style() method
$style .= $value;
else
$style .= $key . ': ' . $value . ';';
}
$attributes['style'] = $style;
if ($style)
$attributes['style'] = $style;
if (empty($attributes['title'])) {
if (!empty($title)) {
@@ -388,6 +392,19 @@ class Medium extends Data implements RenderableInterface
return $this->link($reset, $attributes);
}
/**
* Allows to add an inline style attribute from Markdown or Twig
* Example: ![Example](myimg.png?style=float:left)
*
* @param string $style
* @return $this
*/
public function style($style)
{
$this->styleAttributes[] = rtrim($style, ';') . ';';
return $this;
}
/**
* Allow any action to be called on this medium from twig or markdown
*
@@ -440,4 +457,5 @@ class Medium extends Data implements RenderableInterface
return $this->_thumbnail;
}
}

View File

@@ -1935,7 +1935,7 @@ class Page
// Format: @command.param
$cmd = $value;
$params = array();
} elseif (is_array($value) && count($value) == 1) {
} elseif (is_array($value) && count($value) == 1 && !is_int(key($value))) {
// Format: @command.param: { attr1: value1, attr2: value2 }
$cmd = (string) key($value);
$params = (array) current($value);
@@ -1957,51 +1957,92 @@ class Page
return $value;
}
/** @var Pages $pages */
$pages = self::getGrav()['pages'];
$parts = explode('.', $cmd);
$current = array_shift($parts);
$results = null;
$results = new Collection();
switch ($current) {
case '@self':
if (!empty($parts)) {
switch ($parts[0]) {
case 'modular':
// @self.modular: false (alternative to @self.children)
if (!empty($params) && $params[0] === false) {
$results = $this->children()->nonModular()->published();
$results = $this->children()->nonModular();
break;
}
$results = $this->children()->modular()->published();
$results = $this->children()->modular();
break;
case 'children':
$results = $this->children()->nonModular()->published();
$results = $this->children()->nonModular();
break;
case 'parent':
$collection = new Collection();
$results = $collection->addPage($this->parent());
break;
case 'siblings':
$results = $this->parent()->children()->remove($this->path());
break;
case 'descendants':
$results = $pages->all($this)->remove($this->path())->nonModular();
break;
}
}
$results = $results->published();
break;
case '@page':
$page = null;
if (!empty($params)) {
/** @var Pages $pages */
$pages = self::getGrav()['pages'];
$page = $this->find($params[0]);
}
list($what, $recurse) = array_pad($params, 2, null);
// safety check in case page is not found
if (!isset($page)) {
return $results;
}
if ($what == '@root') {
$page = $pages->root();
} else {
$page = $this->find($what);
// Handle a @page.descendants
if (!empty($parts)) {
switch ($parts[0]) {
case 'self':
$results = new Collection();
$results = $results->addPage($page);
break;
case 'descendants':
$results = $pages->all($page)->remove($page->path());
break;
case 'children':
$results = $page->children();
break;
}
} else {
$results = $page->children();
}
if ($page) {
if ($recurse) {
$results = $pages->all($page)->nonModular()->published();
} else {
$results = $page->children()->nonModular()->published();
}
}
$results = $results->nonModular()->published();
break;
case '@root':
if (!empty($parts) && $parts[0] == 'descendants') {
$results = $pages->all($pages->root())->nonModular()->published();
} else {
$results = $pages->root()->children()->nonModular()->published();
}
break;
case '@taxonomy':
// Gets a collection of pages by using one of the following formats:
// @taxonomy.category: blog

View File

@@ -272,6 +272,10 @@ class Pages
{
// Fetch page if there's a defined route to it.
$page = isset($this->routes[$url]) ? $this->get($this->routes[$url]) : null;
// Try without trailing slash
if (!$page && Utils::endsWith($url, '/')) {
$page = isset($this->routes[rtrim($url, '/')]) ? $this->get($this->routes[rtrim($url, '/')]) : null;
}
// Are we in the admin? this is important!
$not_admin = !isset($this->grav['admin']);

View File

@@ -171,6 +171,8 @@ class Themes extends Iterator
exit("Theme '$name' does not exist, unable to display page.");
}
$this->config->set('theme', $config->get('themes.' . $name));
if (empty($class)) {
$class = new Theme($grav, $config, $name);
}

View File

@@ -133,11 +133,15 @@ class Uri
// set the original basename
$this->basename = $parts['basename'];
// set the extension
if (isset($parts['extension'])) {
$this->extension = $parts['extension'];
}
$valid_page_types = implode('|', $config->get('system.pages.types'));
if (preg_match("/\.(".$valid_page_types.")$/", $parts['basename'])) {
$uri = rtrim(str_replace(DIRECTORY_SEPARATOR, DS, $parts['dirname']), DS). '/' .$parts['filename'];
$this->extension = $parts['extension'];
}
// set the new url