diff --git a/.gitignore b/.gitignore
index 755814004..197774ee6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -16,6 +16,8 @@ logs/*
!logs/.*
images/*
!images/.*
+user/accounts/*
+!user/accounts/.*
user/data/*
!user/data/.*
user/plugins/*
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 2fc9ca4b7..e13155533 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,33 @@
+# v1.0.0
+## 12/11/2015
+
+1. [](#new)
+ * Add new link attributes via markdown media
+ * Added setters to set state of CSS/JS pipelining
+ * Added `user/accounts` to `.gitignore`
+ * Added configurable permissions option for Image cache
+1. [](#improved)
+ * Hungarian translation updated
+ * Refactored Theme initialization for improved flexibility
+ * Wrapped security section of account blueprints in an 'super user' authorize check
+ * Minor performance optimizations
+ * Updated core page blueprints with markdown preview option
+ * Added useful cache info output to Debugbar
+ * Added `iconv` polyfill library used by Symfony 2.8
+ * Force lowercase of username in a few places for case sensitive filesystems
+1. [](#bugfix)
+ * Fix for GPM problems "Call to a member function set() on null"
+ * Fix for individual asset pipeline values not functioning
+ * Fix `Page::copy()` and `Page::move()` to support multiple moves at once
+ * Fixed page moving of a page with no content
+ * Fix for wrong ordering when moving many pages
+ * Escape root path in page medium files to work with special characters
+ * Add missing parent constructor to Themes class
+ * Fix missing file error in `bin/grav sandbox` command
+ * Fixed changelog differ when upgrading Grav
+ * Fixed a logic error in `Validation->validate()`
+ * Make `$container` available in `setup.php` to fix multi-site
+
# v1.0.0-rc.6
## 12/01/2015
@@ -78,7 +108,7 @@
1. [](#new)
* New Page collection options! `@self.parent, @self.siblings, @self.descendants` + more
- * Whitelist of file types for fallback route functionality (images by default)
+ * White list of file types for fallback route functionality (images by default)
1. [](#improved)
* Assets switched from defines to streams
1. [](#bugfix)
diff --git a/README.md b/README.md
index 8a405e3dc..18c060dbf 100644
--- a/README.md
+++ b/README.md
@@ -103,7 +103,7 @@ What you mainly want to know is that:
# License
-See [LICENSE](LICENSE)
+See [LICENSE](LICENSE.txt)
[gitflow-model]: http://nvie.com/posts/a-successful-git-branching-model/
diff --git a/bin/gpm b/bin/gpm
index 5a039e597..f9f763624 100755
--- a/bin/gpm
+++ b/bin/gpm
@@ -40,8 +40,6 @@ if (!function_exists('curl_version')) {
$grav = Grav::instance(array('loader' => $autoload));
$grav['config']->init();
$grav['streams'];
-$grav['plugins']->init();
-$grav['themes']->init();
$app = new Application('Grav Package Manager', GRAV_VERSION);
$app->addCommands(array(
diff --git a/composer.json b/composer.json
index 86116ed1f..302cabfdc 100644
--- a/composer.json
+++ b/composer.json
@@ -7,14 +7,15 @@
"license": "MIT",
"require": {
"php": ">=5.4.0",
- "twig/twig": "~1.16",
+ "twig/twig": "~1.23",
"erusev/parsedown-extra": "~0.7",
- "symfony/yaml": "~2.7",
- "symfony/console": "~2.7",
- "symfony/event-dispatcher": "~2.7",
- "symfony/var-dumper": "~2.7",
- "doctrine/cache": "~1.4",
- "filp/whoops": "1.2.*@dev",
+ "symfony/yaml": "~2.8",
+ "symfony/console": "~2.8",
+ "symfony/event-dispatcher": "~2.8",
+ "symfony/var-dumper": "~2.8",
+ "symfony/polyfill-iconv": "~1.0",
+ "doctrine/cache": "~1.5",
+ "filp/whoops": "1.1.10",
"monolog/monolog": "~1.0",
"gregwar/image": "~2.0",
"ircmaxell/password-compat": "1.0.*",
diff --git a/composer.lock b/composer.lock
index d19e9a3f8..3483fa781 100644
--- a/composer.lock
+++ b/composer.lock
@@ -1,23 +1,24 @@
{
"_readme": [
"This file locks the dependencies of your project to a known state",
- "Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
+ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
"This file is @generated automatically"
],
- "hash": "b1323e540382de7390663756b3a87de7",
+ "hash": "09fcc6b4528be7d9c8af68a66e85f0b2",
+ "content-hash": "69bee250cbc5160401d50cc47c8d6aba",
"packages": [
{
"name": "doctrine/cache",
- "version": "v1.5.1",
+ "version": "v1.5.2",
"source": {
"type": "git",
"url": "https://github.com/doctrine/cache.git",
- "reference": "2b9cec5a5e722010cbebc91713d4c11eaa064d5e"
+ "reference": "47c7128262da274f590ae6f86eb137a7a64e82af"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/doctrine/cache/zipball/2b9cec5a5e722010cbebc91713d4c11eaa064d5e",
- "reference": "2b9cec5a5e722010cbebc91713d4c11eaa064d5e",
+ "url": "https://api.github.com/repos/doctrine/cache/zipball/47c7128262da274f590ae6f86eb137a7a64e82af",
+ "reference": "47c7128262da274f590ae6f86eb137a7a64e82af",
"shasum": ""
},
"require": {
@@ -74,7 +75,7 @@
"cache",
"caching"
],
- "time": "2015-11-02 18:35:48"
+ "time": "2015-12-03 10:50:37"
},
{
"name": "donatj/phpuseragentparser",
@@ -212,20 +213,20 @@
},
{
"name": "filp/whoops",
- "version": "dev-master",
+ "version": "1.1.10",
"source": {
"type": "git",
"url": "https://github.com/filp/whoops.git",
- "reference": "9a393ceb80f7497b6513feb574638e87048fed55"
+ "reference": "72538eeb70bbfb11964412a3d098d109efd012f7"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/filp/whoops/zipball/9a393ceb80f7497b6513feb574638e87048fed55",
- "reference": "9a393ceb80f7497b6513feb574638e87048fed55",
+ "url": "https://api.github.com/repos/filp/whoops/zipball/72538eeb70bbfb11964412a3d098d109efd012f7",
+ "reference": "72538eeb70bbfb11964412a3d098d109efd012f7",
"shasum": ""
},
"require": {
- "php": ">=5.3.3"
+ "php": ">=5.3.0"
},
"require-dev": {
"mockery/mockery": "0.9.*"
@@ -266,7 +267,7 @@
"whoops",
"zf2"
],
- "time": "2015-09-27 09:47:06"
+ "time": "2015-06-29 05:42:04"
},
{
"name": "gregwar/cache",
@@ -403,25 +404,25 @@
},
{
"name": "maximebf/debugbar",
- "version": "v1.10.5",
+ "version": "v1.11.0",
"source": {
"type": "git",
"url": "https://github.com/maximebf/php-debugbar.git",
- "reference": "30e53e8a28284b69dd223c9f5ee8957befd72636"
+ "reference": "07741d84d39d10f00551c94284cdefcc69703e77"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/maximebf/php-debugbar/zipball/30e53e8a28284b69dd223c9f5ee8957befd72636",
- "reference": "30e53e8a28284b69dd223c9f5ee8957befd72636",
+ "url": "https://api.github.com/repos/maximebf/php-debugbar/zipball/07741d84d39d10f00551c94284cdefcc69703e77",
+ "reference": "07741d84d39d10f00551c94284cdefcc69703e77",
"shasum": ""
},
"require": {
"php": ">=5.3.0",
- "psr/log": "~1.0",
- "symfony/var-dumper": "~2.6"
+ "psr/log": "^1.0",
+ "symfony/var-dumper": "^2.6|^3.0"
},
"require-dev": {
- "phpunit/phpunit": "~4.0"
+ "phpunit/phpunit": "^4.0|^5.0"
},
"suggest": {
"kriswallsmith/assetic": "The best way to manage assets",
@@ -431,12 +432,12 @@
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "1.10-dev"
+ "dev-master": "1.11-dev"
}
},
"autoload": {
- "psr-0": {
- "DebugBar": "src/"
+ "psr-4": {
+ "DebugBar\\": "src/DebugBar/"
}
},
"notification-url": "https://packagist.org/downloads/",
@@ -448,14 +449,19 @@
"name": "Maxime Bouroumeau-Fuseau",
"email": "maxime.bouroumeau@gmail.com",
"homepage": "http://maximebf.com"
+ },
+ {
+ "name": "Barry vd. Heuvel",
+ "email": "barryvdh@gmail.com"
}
],
"description": "Debug bar in the browser for php application",
"homepage": "https://github.com/maximebf/php-debugbar",
"keywords": [
- "debug"
+ "debug",
+ "debugbar"
],
- "time": "2015-10-19 20:35:12"
+ "time": "2015-12-10 09:50:24"
},
{
"name": "monolog/monolog",
@@ -714,25 +720,26 @@
},
{
"name": "symfony/console",
- "version": "v2.7.7",
+ "version": "v2.8.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/console.git",
- "reference": "16bb1cb86df43c90931df65f529e7ebd79636750"
+ "reference": "d232bfc100dfd32b18ccbcab4bcc8f28697b7e41"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/console/zipball/16bb1cb86df43c90931df65f529e7ebd79636750",
- "reference": "16bb1cb86df43c90931df65f529e7ebd79636750",
+ "url": "https://api.github.com/repos/symfony/console/zipball/d232bfc100dfd32b18ccbcab4bcc8f28697b7e41",
+ "reference": "d232bfc100dfd32b18ccbcab4bcc8f28697b7e41",
"shasum": ""
},
"require": {
- "php": ">=5.3.9"
+ "php": ">=5.3.9",
+ "symfony/polyfill-mbstring": "~1.0"
},
"require-dev": {
"psr/log": "~1.0",
- "symfony/event-dispatcher": "~2.1",
- "symfony/process": "~2.1"
+ "symfony/event-dispatcher": "~2.1|~3.0.0",
+ "symfony/process": "~2.1|~3.0.0"
},
"suggest": {
"psr/log": "For using the console logger",
@@ -742,7 +749,7 @@
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "2.7-dev"
+ "dev-master": "2.8-dev"
}
},
"autoload": {
@@ -769,20 +776,20 @@
],
"description": "Symfony Console Component",
"homepage": "https://symfony.com",
- "time": "2015-11-18 09:54:26"
+ "time": "2015-11-30 12:35:10"
},
{
"name": "symfony/event-dispatcher",
- "version": "v2.7.7",
+ "version": "v2.8.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/event-dispatcher.git",
- "reference": "7e2f9c31645680026c2372edf66f863fc7757af5"
+ "reference": "a5eb815363c0388e83247e7e9853e5dbc14999cc"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/7e2f9c31645680026c2372edf66f863fc7757af5",
- "reference": "7e2f9c31645680026c2372edf66f863fc7757af5",
+ "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/a5eb815363c0388e83247e7e9853e5dbc14999cc",
+ "reference": "a5eb815363c0388e83247e7e9853e5dbc14999cc",
"shasum": ""
},
"require": {
@@ -790,10 +797,10 @@
},
"require-dev": {
"psr/log": "~1.0",
- "symfony/config": "~2.0,>=2.0.5",
- "symfony/dependency-injection": "~2.6",
- "symfony/expression-language": "~2.6",
- "symfony/stopwatch": "~2.3"
+ "symfony/config": "~2.0,>=2.0.5|~3.0.0",
+ "symfony/dependency-injection": "~2.6|~3.0.0",
+ "symfony/expression-language": "~2.6|~3.0.0",
+ "symfony/stopwatch": "~2.3|~3.0.0"
},
"suggest": {
"symfony/dependency-injection": "",
@@ -802,7 +809,7 @@
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "2.7-dev"
+ "dev-master": "2.8-dev"
}
},
"autoload": {
@@ -829,24 +836,140 @@
],
"description": "Symfony EventDispatcher Component",
"homepage": "https://symfony.com",
- "time": "2015-10-30 20:10:21"
+ "time": "2015-10-30 20:15:42"
},
{
- "name": "symfony/var-dumper",
- "version": "v2.7.7",
+ "name": "symfony/polyfill-iconv",
+ "version": "v1.0.0",
"source": {
"type": "git",
- "url": "https://github.com/symfony/var-dumper.git",
- "reference": "72bcb27411780eaee9469729aace73c0d46fb2b8"
+ "url": "https://github.com/symfony/polyfill-iconv.git",
+ "reference": "21a18998764e569c1675efc7191887130b319605"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/var-dumper/zipball/72bcb27411780eaee9469729aace73c0d46fb2b8",
- "reference": "72bcb27411780eaee9469729aace73c0d46fb2b8",
+ "url": "https://api.github.com/repos/symfony/polyfill-iconv/zipball/21a18998764e569c1675efc7191887130b319605",
+ "reference": "21a18998764e569c1675efc7191887130b319605",
"shasum": ""
},
"require": {
- "php": ">=5.3.9"
+ "php": ">=5.3.3"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.0-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Polyfill\\Iconv\\": ""
+ },
+ "files": [
+ "bootstrap.php"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Symfony polyfill for the Iconv extension",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "compatibility",
+ "iconv",
+ "polyfill",
+ "portable",
+ "shim"
+ ],
+ "time": "2015-11-04 20:28:58"
+ },
+ {
+ "name": "symfony/polyfill-mbstring",
+ "version": "v1.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/polyfill-mbstring.git",
+ "reference": "0b6a8940385311a24e060ec1fe35680e17c74497"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/0b6a8940385311a24e060ec1fe35680e17c74497",
+ "reference": "0b6a8940385311a24e060ec1fe35680e17c74497",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.3.3"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.0-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Polyfill\\Mbstring\\": ""
+ },
+ "files": [
+ "bootstrap.php"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Symfony polyfill for the Mbstring extension",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "compatibility",
+ "mbstring",
+ "polyfill",
+ "portable",
+ "shim"
+ ],
+ "time": "2015-11-04 20:28:58"
+ },
+ {
+ "name": "symfony/var-dumper",
+ "version": "v2.8.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/var-dumper.git",
+ "reference": "e6f3855005f2bfad7d7e72431d374a6478893fe3"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/var-dumper/zipball/e6f3855005f2bfad7d7e72431d374a6478893fe3",
+ "reference": "e6f3855005f2bfad7d7e72431d374a6478893fe3",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.3.9",
+ "symfony/polyfill-mbstring": "~1.0"
+ },
+ "require-dev": {
+ "twig/twig": "~1.20|~2.0"
},
"suggest": {
"ext-symfony_debug": ""
@@ -854,7 +977,7 @@
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "2.7-dev"
+ "dev-master": "2.8-dev"
}
},
"autoload": {
@@ -888,20 +1011,20 @@
"debug",
"dump"
],
- "time": "2015-11-18 13:41:01"
+ "time": "2015-11-18 13:45:00"
},
{
"name": "symfony/yaml",
- "version": "v2.7.7",
+ "version": "v2.8.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/yaml.git",
- "reference": "4cfcd7a9fceba662b3c036b7d9a91f6197af046c"
+ "reference": "f79824187de95064a2f5038904c4d7f0227fedb5"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/yaml/zipball/4cfcd7a9fceba662b3c036b7d9a91f6197af046c",
- "reference": "4cfcd7a9fceba662b3c036b7d9a91f6197af046c",
+ "url": "https://api.github.com/repos/symfony/yaml/zipball/f79824187de95064a2f5038904c4d7f0227fedb5",
+ "reference": "f79824187de95064a2f5038904c4d7f0227fedb5",
"shasum": ""
},
"require": {
@@ -910,7 +1033,7 @@
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "2.7-dev"
+ "dev-master": "2.8-dev"
}
},
"autoload": {
@@ -937,7 +1060,7 @@
],
"description": "Symfony Yaml Component",
"homepage": "https://symfony.com",
- "time": "2015-11-18 13:41:01"
+ "time": "2015-11-30 12:35:10"
},
{
"name": "twig/twig",
@@ -1004,9 +1127,7 @@
"packages-dev": [],
"aliases": [],
"minimum-stability": "stable",
- "stability-flags": {
- "filp/whoops": 20
- },
+ "stability-flags": [],
"prefer-stable": false,
"prefer-lowest": false,
"platform": {
diff --git a/system/blueprints/config/system.yaml b/system/blueprints/config/system.yaml
index 78ac4d5ca..63b13ac92 100644
--- a/system/blueprints/config/system.yaml
+++ b/system/blueprints/config/system.yaml
@@ -516,6 +516,17 @@ form:
validate:
type: bool
+ twig.umask_fix:
+ type: toggle
+ label: PLUGIN_ADMIN.TWIG_UMASK_FIX
+ help: PLUGIN_ADMIN.TWIG_UMASK_FIX_HELP
+ highlight: 0
+ options:
+ 1: PLUGIN_ADMIN.YES
+ 0: PLUGIN_ADMIN.NO
+ validate:
+ type: bool
+
assets:
type: section
title: PLUGIN_ADMIN.ASSETS
@@ -688,6 +699,17 @@ form:
validate:
type: bool
+ images.cache_perms:
+ type: select
+ size: small
+ label: PLUGIN_ADMIN.CACHE_PERMS
+ help: PLUGIN_ADMIN.CACHE_PERMS_HELP
+ highlight: '0755'
+ options:
+ 1: '0755'
+ 0: '0775'
+
+
images.debug:
type: toggle
label: PLUGIN_ADMIN.IMAGES_DEBUG
@@ -745,13 +767,14 @@ form:
fields:
session.enabled:
- type: toggle
+ type: hidden
label: PLUGIN_ADMIN.ENABLED
help: PLUGIN_ADMIN.SESSION_ENABLED_HELP
highlight: 1
options:
1: PLUGIN_ADMIN.YES
0: PLUGIN_ADMIN.NO
+ default: true
validate:
type: bool
diff --git a/system/blueprints/pages/default.yaml b/system/blueprints/pages/default.yaml
index 7be8a6fdd..d421bbd62 100644
--- a/system/blueprints/pages/default.yaml
+++ b/system/blueprints/pages/default.yaml
@@ -29,6 +29,7 @@ form:
content:
type: markdown
+ showPreview: true
validate:
type: textarea
diff --git a/system/blueprints/pages/modular_raw.yaml b/system/blueprints/pages/modular_raw.yaml
index 430881712..393cde5fd 100644
--- a/system/blueprints/pages/modular_raw.yaml
+++ b/system/blueprints/pages/modular_raw.yaml
@@ -25,6 +25,7 @@ form:
content:
type: markdown
+ showPreview: true
uploads:
type: pagemedia
diff --git a/system/blueprints/pages/raw.yaml b/system/blueprints/pages/raw.yaml
index d26f05063..bed82d593 100644
--- a/system/blueprints/pages/raw.yaml
+++ b/system/blueprints/pages/raw.yaml
@@ -25,6 +25,7 @@ form:
content:
type: markdown
+ showPreview: true
uploads:
type: pagemedia
diff --git a/system/blueprints/user/account.yaml b/system/blueprints/user/account.yaml
index 83674c721..81a659d0c 100644
--- a/system/blueprints/user/account.yaml
+++ b/system/blueprints/user/account.yaml
@@ -54,26 +54,32 @@ form:
default: 'en'
help: PLUGIN_ADMIN.LANGUAGE_HELP
- groups:
- type: selectize
- size: large
- label: PLUGIN_ADMIN.GROUPS
- '@data-options': '\Grav\User\Groups::groups'
- classes: fancy
- help: PLUGIN_ADMIN.GROUPS_HELP
- validate:
- type: commalist
+ security:
+ title: Security
+ type: section
+ security: admin.super
- access.admin:
- type: array
- label: PLUGIN_ADMIN.ADMIN_ACCESS
- multiple: false
- validate:
- type: array
+ fields:
+ groups:
+ type: selectize
+ size: large
+ label: PLUGIN_ADMIN.GROUPS
+ '@data-options': '\Grav\User\Groups::groups'
+ classes: fancy
+ help: PLUGIN_ADMIN.GROUPS_HELP
+ validate:
+ type: commalist
- access.site:
- type: array
- label: PLUGIN_ADMIN.SITE_ACCESS
- multiple: false
- validate:
- type: array
\ No newline at end of file
+ access.admin:
+ type: array
+ label: PLUGIN_ADMIN.ADMIN_ACCESS
+ multiple: false
+ validate:
+ type: array
+
+ access.site:
+ type: array
+ label: PLUGIN_ADMIN.SITE_ACCESS
+ multiple: false
+ validate:
+ type: array
\ No newline at end of file
diff --git a/system/config/system.yaml b/system/config/system.yaml
index f64620cbf..652524902 100644
--- a/system/config/system.yaml
+++ b/system/config/system.yaml
@@ -97,6 +97,7 @@ debugger:
images:
default_image_quality: 85 # Default image quality to use when resampling images (85%)
cache_all: false # Cache all image by default
+ cache_perms: 0755 # Default cache folder perms. Usually 0755 or 0775 depending on setup
debug: false # Show an overlay over images indicating the pixel depth of the image when working with retina for example
media:
diff --git a/system/defines.php b/system/defines.php
index 2e0e01442..df48dff23 100644
--- a/system/defines.php
+++ b/system/defines.php
@@ -2,7 +2,7 @@
// Some standard defines
define('GRAV', true);
-define('GRAV_VERSION', '1.0.0-rc.6');
+define('GRAV_VERSION', '1.0.0');
define('DS', '/');
// Directories and Paths
diff --git a/system/languages/hu.yaml b/system/languages/hu.yaml
index 71c0ee91e..e807896eb 100644
--- a/system/languages/hu.yaml
+++ b/system/languages/hu.yaml
@@ -50,3 +50,4 @@ NICETIME:
FORM:
VALIDATION_FAIL: A validáció hibát talált:
INVALID_INPUT: Az itt megadott érték érvénytelen:
+ MISSING_REQUIRED_FIELD: Ez a kötelező mező nincs kitöltve:
diff --git a/system/src/Grav/Common/Assets.php b/system/src/Grav/Common/Assets.php
index 16822b990..be6b81f7a 100644
--- a/system/src/Grav/Common/Assets.php
+++ b/system/src/Grav/Common/Assets.php
@@ -203,7 +203,7 @@ class Assets
*
* @return $this
*/
- public function add($asset, $priority = null, $pipeline = null)
+ public function add($asset, $priority = null, $pipeline = true)
{
// More than one asset
if (is_array($asset)) {
@@ -243,7 +243,7 @@ class Assets
*
* @return $this
*/
- public function addCss($asset, $priority = null, $pipeline = null, $group = null)
+ public function addCss($asset, $priority = null, $pipeline = true, $group = null)
{
if (is_array($asset)) {
foreach ($asset as $a) {
@@ -259,16 +259,20 @@ class Assets
$asset = $this->buildLocalLink($asset);
}
+ // Check for existence
+ if ($asset === false) {
+ return $this;
+ }
+
$data = [
'asset' => $asset,
'priority' => intval($priority ?: 10),
'order' => count($this->css),
- 'pipeline' => $pipeline ?: true,
+ 'pipeline' => (bool) $pipeline,
'group' => $group ?: 'head'
];
// check for dynamic array and merge with defaults
- $count_args = func_num_args();
if (func_num_args() == 2) {
$dynamic_arg = func_get_arg(1);
if (is_array($dynamic_arg)) {
@@ -297,7 +301,7 @@ class Assets
* @param string $group name of the group
* @return $this
*/
- public function addJs($asset, $priority = null, $pipeline = null, $loading = null, $group = null)
+ public function addJs($asset, $priority = null, $pipeline = true, $loading = null, $group = null)
{
if (is_array($asset)) {
foreach ($asset as $a) {
@@ -313,17 +317,21 @@ class Assets
$asset = $this->buildLocalLink($asset);
}
+ // Check for existence
+ if ($asset === false) {
+ return $this;
+ }
+
$data = [
'asset' => $asset,
'priority' => intval($priority ?: 10),
'order' => count($this->js),
- 'pipeline' => $pipeline ?: true,
+ 'pipeline' => (bool) $pipeline,
'loading' => $loading ?: '',
'group' => $group ?: 'head'
];
// check for dynamic array and merge with defaults
- $count_args = func_num_args();
if (func_num_args() == 2) {
$dynamic_arg = func_get_arg(1);
if (is_array($dynamic_arg)) {
@@ -351,7 +359,7 @@ class Assets
*
* @return \Grav\Common\Assets
*/
- public function addAsyncJs($asset, $priority = null, $pipeline = null, $group = null)
+ public function addAsyncJs($asset, $priority = null, $pipeline = true, $group = null)
{
return $this->addJs($asset, $priority, $pipeline, 'async', $group);
}
@@ -368,7 +376,7 @@ class Assets
*
* @return \Grav\Common\Assets
*/
- public function addDeferJs($asset, $priority = null, $pipeline = null, $group = null)
+ public function addDeferJs($asset, $priority = null, $pipeline = true, $group = null)
{
return $this->addJs($asset, $priority, $pipeline, 'defer', $group);
}
@@ -1124,6 +1132,36 @@ class Assets
return $this->addDir($directory, self::JS_REGEX);
}
+ /**
+ * Sets the state of CSS Pipeline
+ *
+ * @param boolean $value
+ */
+ public function setCssPipeline($value)
+ {
+ $this->css_pipeline = (bool) $value;
+ }
+
+ /**
+ * Sets the state of JS Pipeline
+ *
+ * @param boolean $value
+ */
+ public function setJsPipeline($value)
+ {
+ $this->js_pipeline = (bool) $value;
+ }
+
+ /**
+ * Explicitly set's a timestamp for assets
+ *
+ * @param $value
+ */
+ public function setTimestamp($value)
+ {
+ $this->timestamp = '?'.$value;
+ }
+
public function __toString()
{
return '';
diff --git a/system/src/Grav/Common/Cache.php b/system/src/Grav/Common/Cache.php
index 6ba75f4a3..da0825ed9 100644
--- a/system/src/Grav/Common/Cache.php
+++ b/system/src/Grav/Common/Cache.php
@@ -38,6 +38,8 @@ class Cache extends Getters
*/
protected $driver;
+ protected $driver_name;
+
/**
* @var bool
*/
@@ -110,6 +112,10 @@ class Cache extends Getters
// 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 . ']');
+
}
/**
@@ -136,6 +142,8 @@ class Cache extends Getters
$driver_name = $setting;
}
+ $this->driver_name = $driver_name;
+
switch ($driver_name) {
case 'apc':
$driver = new \Doctrine\Common\Cache\ApcCache();
diff --git a/system/src/Grav/Common/Config/Setup.php b/system/src/Grav/Common/Config/Setup.php
index 90178d6b2..b345426c1 100644
--- a/system/src/Grav/Common/Config/Setup.php
+++ b/system/src/Grav/Common/Config/Setup.php
@@ -113,8 +113,13 @@ class Setup extends Data
],
];
- public function __construct($environment = 'localhost')
+ public function __construct($container)
{
+ $environment = $container['uri']->environment();
+ if (!$environment) {
+ $environment = 'localhost';
+ }
+
// Pre-load setup.php which contains our initial configuration.
// Configuration may contain dynamic parts, which is why we need to always load it.
$file = GRAV_ROOT . '/setup.php';
diff --git a/system/src/Grav/Common/Data/Validation.php b/system/src/Grav/Common/Data/Validation.php
index 9cebd0d76..9e62802b1 100644
--- a/system/src/Grav/Common/Data/Validation.php
+++ b/system/src/Grav/Common/Data/Validation.php
@@ -32,7 +32,7 @@ class Validation
}
// special case for files, value is never empty and errors with code 4 instead
- if (empty($validate['required']) && $field['type'] == 'file' && (isset($value['error']) && ($value['error'] == UPLOAD_ERR_NO_FILE) || in_array(UPLOAD_ERR_NO_FILE, $value['error']))) {
+ if (empty($validate['required']) && $field['type'] == 'file' && (isset($value['error']) && ($value['error'] == UPLOAD_ERR_NO_FILE || in_array(UPLOAD_ERR_NO_FILE, $value['error'])))) {
return;
}
@@ -43,7 +43,7 @@ class Validation
$type = (string) isset($field['validate']['type']) ? $field['validate']['type'] : $field['type'];
$method = 'type'.strtr($type, '-', '_');
$name = ucfirst(isset($field['label']) ? $field['label'] : $field['name']);
- $message = (string) isset($field['validate']['message']) ? $field['validate']['message'] : $language->translate('FORM.INVALID_INPUT', null, true) . ' "' . $language->translate($name) . '"';
+ $message = (string) isset($field['validate']['message']) ? $language->translate($field['validate']['message']) : $language->translate('FORM.INVALID_INPUT', null, true) . ' "' . $language->translate($name) . '"';
if (method_exists(__CLASS__, $method)) {
$success = self::$method($value, $validate, $field);
@@ -84,7 +84,7 @@ class Validation
}
// special case for files, value is never empty and errors with code 4 instead
- if (empty($validate['required']) && $field['type'] == 'file' && (isset($value['error']) && ($value['error'] == UPLOAD_ERR_NO_FILE) || in_array(UPLOAD_ERR_NO_FILE, $value['error']))) {
+ if (empty($validate['required']) && $field['type'] == 'file' && (isset($value['error']) && ($value['error'] == UPLOAD_ERR_NO_FILE || in_array(UPLOAD_ERR_NO_FILE, $value['error'])))) {
return null;
}
@@ -620,7 +620,7 @@ class Validation
if (is_string($value)) {
$value = trim($value);
}
-
+
return (bool) $params !== true || !empty($value);
}
diff --git a/system/src/Grav/Common/Filesystem/Folder.php b/system/src/Grav/Common/Filesystem/Folder.php
index 62d610b65..81d610898 100644
--- a/system/src/Grav/Common/Filesystem/Folder.php
+++ b/system/src/Grav/Common/Filesystem/Folder.php
@@ -274,6 +274,11 @@ abstract class Folder
throw new \RuntimeException('Cannot move non-existing folder.');
}
+ // Don't do anything if the source is the same as the new target
+ if ($source == $target) {
+ return;
+ }
+
// Make sure that path to the target exists before moving.
self::create(dirname($target));
diff --git a/system/src/Grav/Common/GPM/Remote/Grav.php b/system/src/Grav/Common/GPM/Remote/Grav.php
index f8148a526..152b8eda7 100644
--- a/system/src/Grav/Common/GPM/Remote/Grav.php
+++ b/system/src/Grav/Common/GPM/Remote/Grav.php
@@ -55,7 +55,7 @@ class Grav extends AbstractPackageCollection
$diffLog = [];
foreach ($this->data['changelog'] as $version => $changelog) {
- preg_match("/[\d\.]+/", $version, $cleanVersion);
+ preg_match("/[\w-\.]+/", $version, $cleanVersion);
if (!$cleanVersion || version_compare($diff, $cleanVersion[0], ">=")) { continue; }
diff --git a/system/src/Grav/Common/Grav.php b/system/src/Grav/Common/Grav.php
index eefef19a1..bb987f751 100644
--- a/system/src/Grav/Common/Grav.php
+++ b/system/src/Grav/Common/Grav.php
@@ -230,7 +230,6 @@ class Grav extends Container
$debugger->startTimer('themes', 'Themes');
$this['themes']->init();
- $this->fireEvent('onThemeInitialized');
$debugger->stopTimer('themes');
$task = $this['task'];
diff --git a/system/src/Grav/Common/Iterator.php b/system/src/Grav/Common/Iterator.php
index d23f42910..287b925ae 100644
--- a/system/src/Grav/Common/Iterator.php
+++ b/system/src/Grav/Common/Iterator.php
@@ -215,4 +215,26 @@ class Iterator implements \ArrayAccess, \Iterator, \Countable, \Serializable
return $this;
}
+
+
+ /**
+ * Sorts elements from the list and returns a copy of the list in the proper order
+ *
+ * @param callable|null $callback
+ *
+ * @param bool $desc
+ *
+ * @return $this|array
+ * @internal param bool $asc
+ *
+ */
+ public function sort(callable $callback = null, $desc = false)
+ {
+ if (!$callback || !is_callable($callback)) { return $this; }
+
+ $items = $this->items;
+ uasort($items, $callback);
+
+ return !$desc ? $items : array_reverse($items, true);
+ }
}
diff --git a/system/src/Grav/Common/Language/Language.php b/system/src/Grav/Common/Language/Language.php
index 5dfe343f2..6a9e3f18c 100644
--- a/system/src/Grav/Common/Language/Language.php
+++ b/system/src/Grav/Common/Language/Language.php
@@ -352,7 +352,7 @@ class Language
if ($this->config->get('system.languages.translations_fallback', true)) {
$languages = $this->getFallbackLanguages();
} else {
- $languages = (array)$this->getDefault();
+ $languages = (array)$this->getLanguage();
}
}
} else {
diff --git a/system/src/Grav/Common/Markdown/ParsedownGravTrait.php b/system/src/Grav/Common/Markdown/ParsedownGravTrait.php
index fd6dd623f..5c075dcb5 100644
--- a/system/src/Grav/Common/Markdown/ParsedownGravTrait.php
+++ b/system/src/Grav/Common/Markdown/ParsedownGravTrait.php
@@ -204,6 +204,48 @@ trait ParsedownGravTrait
if (isset($excerpt['element']['attributes']['href'])) {
$url = parse_url(htmlspecialchars_decode($excerpt['element']['attributes']['href']));
+ // if there is a query, then parse it and build action calls
+ if (isset($url['query'])) {
+ $actions = array_reduce(explode('&', $url['query']), function ($carry, $item) {
+ $parts = explode('=', $item, 2);
+ $value = isset($parts[1]) ? $parts[1] : null;
+ $carry[$parts[0]] = $value;
+
+ return $carry;
+ }, []);
+
+ // valid attributes supported
+ $valid_attributes = ['rel', 'target', 'id', 'class', 'classes'];
+
+ // Unless told to not process, go through actions
+ if (array_key_exists('noprocess', $actions)) {
+ unset($actions['noprocess']);
+ } else {
+ // loop through actions for the image and call them
+ foreach ($actions as $attrib => $value) {
+ $key = $attrib;
+
+ if (in_array($attrib, $valid_attributes)) {
+ // support both class and classes
+ if ($attrib == 'classes') {
+ $attrib = 'class';
+ }
+ $excerpt['element']['attributes'][$attrib] = $value;
+ unset($actions[$key]);
+ }
+ }
+ }
+
+
+ $url['query']= http_build_query($actions, null, '&', PHP_QUERY_RFC3986);
+ }
+
+ // if no query elements left, unset query
+ if (empty($url['query'])) {
+ unset ($url['query']);
+ }
+
+
// if there is no scheme, the file is local
if (!isset($url['scheme']) && (count($url) > 0)) {
// convert the URl is required
diff --git a/system/src/Grav/Common/Page/Medium/ImageFile.php b/system/src/Grav/Common/Page/Medium/ImageFile.php
index 8e59f6d7b..c02922b30 100644
--- a/system/src/Grav/Common/Page/Medium/ImageFile.php
+++ b/system/src/Grav/Common/Page/Medium/ImageFile.php
@@ -55,6 +55,7 @@ class ImageFile extends Image
$cacheFile .= $this->prettyName;
}
+
$cacheFile .= '.'.$type;
// If the files does not exists, save it
@@ -79,7 +80,8 @@ class ImageFile extends Image
// Asking the cache for the cacheFile
try {
- $file = $this->cache->getOrCreateFile($cacheFile, $conditions, $generate, $actual);
+ $perms = octdec(self::getGrav()['config']->get('system.images.cache_perms', '0755'));
+ $file = $this->cache->setDirectoryMode($perms)->getOrCreateFile($cacheFile, $conditions, $generate, $actual);
} catch (GenerationError $e) {
$file = $e->getNewFile();
}
diff --git a/system/src/Grav/Common/Page/Medium/ImageMedium.php b/system/src/Grav/Common/Page/Medium/ImageMedium.php
index 85e3d63b6..3db039643 100644
--- a/system/src/Grav/Common/Page/Medium/ImageMedium.php
+++ b/system/src/Grav/Common/Page/Medium/ImageMedium.php
@@ -135,7 +135,7 @@ class ImageMedium extends Medium
*/
public function url($reset = true)
{
- $output = preg_replace('|^' . GRAV_ROOT . '|', '', $this->saveImage());
+ $output = preg_replace('|^' . preg_quote(GRAV_ROOT) . '|', '', $this->saveImage());
if ($reset) {
$this->reset();
diff --git a/system/src/Grav/Common/Page/Medium/Medium.php b/system/src/Grav/Common/Page/Medium/Medium.php
index a5e4001a7..714eeb5e8 100644
--- a/system/src/Grav/Common/Page/Medium/Medium.php
+++ b/system/src/Grav/Common/Page/Medium/Medium.php
@@ -137,7 +137,7 @@ class Medium extends Data implements RenderableInterface
*/
public function url($reset = true)
{
- $output = preg_replace('|^' . GRAV_ROOT . '|', '', $this->get('filepath'));
+ $output = preg_replace('|^' . preg_quote(GRAV_ROOT) . '|', '', $this->get('filepath'));
if ($reset) {
$this->reset();
diff --git a/system/src/Grav/Common/Page/Page.php b/system/src/Grav/Common/Page/Page.php
index 50b3fa83f..a6e4282e9 100644
--- a/system/src/Grav/Common/Page/Page.php
+++ b/system/src/Grav/Common/Page/Page.php
@@ -5,6 +5,7 @@ use Exception;
use Grav\Common\Filesystem\Folder;
use Grav\Common\Config\Config;
use Grav\Common\GravTrait;
+use Grav\Common\Language\Language;
use Grav\Common\Utils;
use Grav\Common\Cache;
use Grav\Common\Twig;
@@ -381,7 +382,7 @@ class Page
*/
public function modifyHeader($key, $value)
{
- $this->header->$key = $value;
+ $this->header->{$key} = $value;
}
/**
@@ -392,8 +393,7 @@ class Page
*/
public function summary($size = null)
{
- /** @var Config $config */
- $config = self::getGrav()['config']->get('site.summary');
+ $config = (array) self::getGrav()['config']->get('site.summary');
if (isset($this->header->summary)) {
$config = array_merge($config, $this->header->summary);
}
@@ -728,27 +728,30 @@ class Page
* You need to call $this->save() in order to perform the move.
*
* @param Page $parent New parent page.
- * @return Page
+ * @return $this
*/
public function move(Page $parent)
{
- $clone = clone $this;
- $clone->_action = 'move';
- $clone->_original = $this;
- $clone->parent($parent);
- $clone->id(time().md5($clone->filePath()));
+ if (!$this->_original) {
+ $clone = clone $this;
+ $this->_original = $clone;
+ }
+
+ $this->_action = 'move';
+ $this->parent($parent);
+ $this->id(time().md5($this->filePath()));
if ($parent->path()) {
- $clone->path($parent->path() . '/' . $clone->folder());
+ $this->path($parent->path() . '/' . $this->folder());
}
if ($parent->route()) {
- $clone->route($parent->route() . '/'. $clone->slug());
+ $this->route($parent->route() . '/'. $this->slug());
} else {
- $clone->route(self::getGrav()['pages']->root()->route() . '/'. $clone->slug());
+ $this->route(self::getGrav()['pages']->root()->route() . '/'. $this->slug());
}
- return $clone;
+ return $this;
}
/**
@@ -758,14 +761,14 @@ class Page
* You need to call $this->save() in order to perform the move.
*
* @param Page $parent New parent page.
- * @return Page
+ * @return $this
*/
public function copy($parent)
{
- $clone = $this->move($parent);
- $clone->_action = 'copy';
+ $this->move($parent);
+ $this->_action = 'copy';
- return $clone;
+ return $this;
}
/**
@@ -826,7 +829,7 @@ class Page
$blueprints = $this->blueprints();
$values = $blueprints->filter($this->toArray());
if ($values && isset($values['header'])) {
- $this->header($values['header']);
+ $this->header($values['header']);
}
}
@@ -1805,11 +1808,13 @@ class Page
if (isset($routes[$uri_path])) {
$child_page = $pages->dispatch($uri->route())->parent();
- if ($child_page) while (!$child_page->root()) {
- if ($this->path() == $child_page->path()) {
- return true;
+ if ($child_page) {
+ while (!$child_page->root()) {
+ if ($this->path() == $child_page->path()) {
+ return true;
+ }
+ $child_page = $child_page->parent();
}
- $child_page = $child_page->parent();
}
}
@@ -1843,7 +1848,7 @@ class Page
/**
* Helper method to return a page.
*
- * @param string $url the url of the page
+ * @param string $url the url of the page
* @param bool $all
*
* @return \Grav\Common\Page\Page page you were looking for if it exists
@@ -1951,7 +1956,7 @@ class Page
* @return mixed
* @internal
*/
- protected function evaluate($value)
+ public function evaluate($value)
{
// Parse command.
if (is_string($value)) {
@@ -2141,7 +2146,7 @@ class Page
*/
protected function doRelocation($reorder)
{
- if (empty($this->_original) ) {
+ if (!$this->_original) {
return;
}
@@ -2182,7 +2187,7 @@ class Page
// Handle all the other pages.
$page = $pages->get($path);
- if ($page && $page->exists() && $page->order() != $order+1) {
+ if ($page && $page->exists() && !$page->_action && $page->order() != $order+1) {
$page = $page->move($parent);
$page->order($order+1);
$page->save(false);
@@ -2190,11 +2195,12 @@ class Page
}
}
}
- if ($this->_action == 'move' && $this->_original->exists()) {
- Folder::move($this->_original->path(), $this->path());
- }
- if ($this->_action == 'copy' && $this->_original->exists()) {
- Folder::copy($this->_original->path(), $this->path());
+ if (is_dir($this->_original->path())) {
+ if ($this->_action == 'move') {
+ Folder::move($this->_original->path(), $this->path());
+ } elseif ($this->_action == 'copy') {
+ Folder::copy($this->_original->path(), $this->path());
+ }
}
if ($this->name() != $this->_original->name()) {
@@ -2204,7 +2210,6 @@ class Page
}
}
- $this->_action = null;
$this->_original = null;
}
diff --git a/system/src/Grav/Common/Page/Pages.php b/system/src/Grav/Common/Page/Pages.php
index efe7ef1c0..7193db791 100644
--- a/system/src/Grav/Common/Page/Pages.php
+++ b/system/src/Grav/Common/Page/Pages.php
@@ -237,7 +237,7 @@ class Pages
/**
* Get a page instance.
*
- * @param string $path
+ * @param string $path The filesystem full path of the page
* @return Page
* @throws \Exception
*/
@@ -264,7 +264,7 @@ class Pages
/**
* Dispatch URI to a page.
*
- * @param $url
+ * @param string $url The relative URL of the page
* @param bool $all
* @return Page|null
*/
diff --git a/system/src/Grav/Common/Plugins.php b/system/src/Grav/Common/Plugins.php
index 84844804c..de479e138 100644
--- a/system/src/Grav/Common/Plugins.php
+++ b/system/src/Grav/Common/Plugins.php
@@ -104,8 +104,12 @@ class Plugins extends Iterator
continue;
}
- $type = $directory->getBasename();
- $list[$type] = self::get($type);
+ $plugin = $directory->getBasename();
+ $result = self::get($plugin);
+
+ if ($result) {
+ $list[$plugin] = $result;
+ }
}
}
ksort($list);
@@ -120,9 +124,9 @@ class Plugins extends Iterator
$blueprint->name = $name;
// Load default configuration.
- $file = CompiledYamlFile::instance("plugins://{$name}/{$name}.yaml");
+ $file = CompiledYamlFile::instance("plugins://{$name}/{$name}" . YAML_EXT);
- // ensure the plugin exists physically
+ // ensure this is a valid plugin
if (!$file->exists()) {
return null;
}
diff --git a/system/src/Grav/Common/Service/ConfigServiceProvider.php b/system/src/Grav/Common/Service/ConfigServiceProvider.php
index febdcd6a7..9efbd7465 100644
--- a/system/src/Grav/Common/Service/ConfigServiceProvider.php
+++ b/system/src/Grav/Common/Service/ConfigServiceProvider.php
@@ -40,7 +40,7 @@ class ConfigServiceProvider implements ServiceProviderInterface
public static function setup(Container $container)
{
- return new Setup($container['uri']->environment());
+ return new Setup($container);
}
public static function blueprints(Container $container)
diff --git a/system/src/Grav/Common/Themes.php b/system/src/Grav/Common/Themes.php
index 4c5748be6..2f020f7a4 100644
--- a/system/src/Grav/Common/Themes.php
+++ b/system/src/Grav/Common/Themes.php
@@ -25,6 +25,8 @@ class Themes extends Iterator
public function __construct(Grav $grav)
{
+ parent::__construct();
+
$this->grav = $grav;
$this->config = $grav['config'];
@@ -34,13 +36,18 @@ class Themes extends Iterator
public function init()
{
- /** @var EventDispatcher $events */
- $events = $this->grav['events'];
-
/** @var Themes $themes */
$themes = $this->grav['themes'];
$themes->configure();
+ $this->initTheme();
+ }
+
+ public function initTheme()
+ {
+ /** @var Themes $themes */
+ $themes = $this->grav['themes'];
+
try {
$instance = $themes->load();
} catch (\InvalidArgumentException $e) {
@@ -48,10 +55,15 @@ class Themes extends Iterator
}
if ($instance instanceof EventSubscriberInterface) {
+ /** @var EventDispatcher $events */
+ $events = $this->grav['events'];
+
$events->addSubscriber($instance);
}
$this->grav['theme'] = $instance;
+
+ $this->grav->fireEvent('onThemeInitialized');
}
/**
@@ -74,8 +86,12 @@ class Themes extends Iterator
continue;
}
- $type = $directory->getBasename();
- $list[$type] = self::get($type);
+ $theme = $directory->getBasename();
+ $result = self::get($theme);
+
+ if ($result) {
+ $list[$theme] = $result;
+ }
}
}
ksort($list);
@@ -100,14 +116,20 @@ class Themes extends Iterator
$blueprint = $blueprints->get("{$name}/blueprints");
$blueprint->name = $name;
+ // Load default configuration.
+ $file = CompiledYamlFile::instance("themes://{$name}/{$name}" . YAML_EXT);
+
+ // ensure this is a valid theme
+ if (!$file->exists()) {
+ return null;
+ }
+
// Find thumbnail.
$thumb = "themes://{$name}/thumbnail.jpg";
if ($path = $this->grav['locator']->findResource($thumb, false)) {
$blueprint->set('thumbnail', $this->grav['base_url'] . '/' . $path);
}
- // Load default configuration.
- $file = CompiledYamlFile::instance("themes://{$name}/{$name}" . YAML_EXT);
$obj = new Data($file->content(), $blueprint);
// Override with user configuration.
diff --git a/system/src/Grav/Common/User/User.php b/system/src/Grav/Common/User/User.php
index 9caad3741..4ba6a20c4 100644
--- a/system/src/Grav/Common/User/User.php
+++ b/system/src/Grav/Common/User/User.php
@@ -32,6 +32,9 @@ class User extends Data
{
$locator = self::getGrav()['locator'];
+ // force lowercase of username
+ $username = strtolower($username);
+
$blueprints = new Blueprints('blueprints://');
$blueprint = $blueprints->get('user/account');
$file_path = $locator->findResource('account://' . $username . YAML_EXT);
diff --git a/system/src/Grav/Console/Cli/SandboxCommand.php b/system/src/Grav/Console/Cli/SandboxCommand.php
index a77bc9720..3b1142646 100644
--- a/system/src/Grav/Console/Cli/SandboxCommand.php
+++ b/system/src/Grav/Console/Cli/SandboxCommand.php
@@ -48,7 +48,7 @@ class SandboxCommand extends ConsoleCommand
'/.editorconfig' => '/.editorconfig',
'/.gitignore' => '/.gitignore',
'/CHANGELOG.md' => '/CHANGELOG.md',
- '/LICENSE' => '/LICENSE',
+ '/LICENSE.txt' => '/LICENSE.txt',
'/README.md' => '/README.md',
'/index.php' => '/index.php',
'/composer.json' => '/composer.json',
diff --git a/system/src/Grav/Console/Gpm/IndexCommand.php b/system/src/Grav/Console/Gpm/IndexCommand.php
index b75c55d59..70a262953 100644
--- a/system/src/Grav/Console/Gpm/IndexCommand.php
+++ b/system/src/Grav/Console/Gpm/IndexCommand.php
@@ -21,6 +21,16 @@ class IndexCommand extends ConsoleCommand
*/
protected $gpm;
+ /**
+ * @var
+ */
+ protected $options;
+
+ /**
+ * @var array
+ */
+ protected $sortKeys = ['name', 'slug', 'author', 'date'];
+
/**
*
*/
@@ -34,6 +44,43 @@ class IndexCommand extends ConsoleCommand
InputOption::VALUE_NONE,
'Force re-fetching the data from remote'
)
+ ->addOption(
+ 'filter',
+ 'F',
+ InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY,
+ 'Allows to limit the results based on one or multiple filters input. This can be either portion of a name/slug or a regex'
+ )
+ ->addOption(
+ 'themes-only',
+ 'T',
+ InputOption::VALUE_NONE,
+ 'Filters the results to only Themes'
+ )
+ ->addOption(
+ 'plugins-only',
+ 'P',
+ InputOption::VALUE_NONE,
+ 'Filters the results to only Plugins'
+ )
+ ->addOption(
+ 'updates-only',
+ 'U',
+ InputOption::VALUE_NONE,
+ 'Filters the results to Updatable Themes and Plugins only'
+ )
+ ->addOption(
+ 'sort',
+ 's',
+ InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY,
+ 'Allows to sort (ASC) the results based on one or multiple keys. SORT can be either "name", "slug", "author", "date"',
+ ['date']
+ )
+ ->addOption(
+ 'desc',
+ 'D',
+ InputOption::VALUE_NONE,
+ 'Reverses the order of the output.'
+ )
->setDescription("Lists the plugins and themes available for installation")
->setHelp('The index command lists the plugins and themes available for installation')
;
@@ -44,16 +91,21 @@ class IndexCommand extends ConsoleCommand
*/
protected function serve()
{
- $this->gpm = new GPM($this->input->getOption('force'));
+ $this->options = $this->input->getOptions();
+
+ $this->gpm = new GPM($this->options['force']);
$this->data = $this->gpm->getRepository();
$this->output->writeln('');
- foreach ($this->data as $type => $packages) {
+ $data = $this->filter($this->data);
+
+ foreach ($data as $type => $packages) {
$this->output->writeln("" . ucfirst($type) . " [ " . count($packages) . " ]");
- $index = 0;
+ $index = 0;
+ $packages = $this->sort($packages);
foreach ($packages as $slug => $package) {
$this->output->writeln(
// index
@@ -108,4 +160,65 @@ class IndexCommand extends ConsoleCommand
return '';
}
+
+ /**
+ * @param $data
+ *
+ * @return mixed
+ */
+ public function filter($data)
+ {
+ // filtering and sorting
+ if ($this->options['plugins-only']) {
+ unset($data['themes']);
+ }
+ if ($this->options['themes-only']) {
+ unset($data['plugins']);
+ }
+
+ if ($this->options['filter'] || $this->options['updates-only'] || $this->options['desc']) {
+ foreach ($data as $type => $packages) {
+ foreach ($packages as $slug => $package) {
+ $filter = true;
+
+ // Filtering by string
+ if ($this->options['filter']) {
+ $filter = preg_grep('/(' . (implode('|', $this->options['filter'])) . ')/i', [$slug, $package->name]);
+ }
+
+ // Filtering updatables only
+ if ($this->options['updates-only'] && $filter) {
+ $method = ucfirst(preg_replace("/s$/", '', $package->package_type));
+ $filter = $this->gpm->{'is' . $method . 'Updatable'}($package->slug);
+ }
+
+ if (!$filter) {
+ unset($data[$type][$slug]);
+ }
+ }
+ }
+ }
+
+ return $data;
+ }
+
+ /**
+ * @param $packages
+ */
+ public function sort($packages)
+ {
+ foreach ($this->options['sort'] as $key) {
+ $packages = $packages->sort(function ($a, $b) use ($key) {
+ switch ($key) {
+ case 'author':
+ return strcmp($a->{$key}['name'], $b->{$key}['name']);
+ break;
+ default:
+ return strcmp($a->$key, $b->$key);
+ }
+ }, $this->options['desc'] ? true : false);
+ }
+
+ return $packages;
+ }
}