From e27f638fe39d340ff74114ad865aaf9eebf4d8f6 Mon Sep 17 00:00:00 2001 From: Flavio Copes Date: Mon, 19 Oct 2015 20:15:11 +0200 Subject: [PATCH 01/79] The User authorize method now checks first if the user group the user belongs to has permissions for the resource (single group version) --- system/src/Grav/Common/User/User.php | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/system/src/Grav/Common/User/User.php b/system/src/Grav/Common/User/User.php index 21cd61c38..260560586 100644 --- a/system/src/Grav/Common/User/User.php +++ b/system/src/Grav/Common/User/User.php @@ -138,6 +138,13 @@ public function authorize($action) return false; } + //Check group access level + $group = $this->get('group'); + if (self::getGrav()['config']->get("site.groups.{$group}.access.{$action}") === true) { + return true; + } + + //Fallback to user access level return $this->get("access.{$action}") === true; } From 77deea8ad47bc42c3e97c003f61a676984c422ae Mon Sep 17 00:00:00 2001 From: Flavio Copes Date: Tue, 20 Oct 2015 15:58:07 +0200 Subject: [PATCH 02/79] Allow a user to be assigned to multiple groups --- system/src/Grav/Common/User/User.php | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/system/src/Grav/Common/User/User.php b/system/src/Grav/Common/User/User.php index 260560586..6fc66abb7 100644 --- a/system/src/Grav/Common/User/User.php +++ b/system/src/Grav/Common/User/User.php @@ -139,9 +139,11 @@ public function authorize($action) } //Check group access level - $group = $this->get('group'); - if (self::getGrav()['config']->get("site.groups.{$group}.access.{$action}") === true) { - return true; + $groups = $this->get('groups'); + foreach($groups as $group) { + if (self::getGrav()['config']->get("site.groups.{$group}.access.{$action}") === true) { + return true; + } } //Fallback to user access level From 3f28dc59ea0c930e37ff22cd1cac2523b828cc10 Mon Sep 17 00:00:00 2001 From: Flavio Copes Date: Tue, 20 Oct 2015 16:07:04 +0200 Subject: [PATCH 03/79] Add check --- system/src/Grav/Common/User/User.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system/src/Grav/Common/User/User.php b/system/src/Grav/Common/User/User.php index 6fc66abb7..a14bfdb1d 100644 --- a/system/src/Grav/Common/User/User.php +++ b/system/src/Grav/Common/User/User.php @@ -140,7 +140,7 @@ public function authorize($action) //Check group access level $groups = $this->get('groups'); - foreach($groups as $group) { + if ($groups) foreach($groups as $group) { if (self::getGrav()['config']->get("site.groups.{$group}.access.{$action}") === true) { return true; } From b82f17f3678c8d9670853c4ed3b09bd9c1d9d771 Mon Sep 17 00:00:00 2001 From: Flavio Copes Date: Tue, 20 Oct 2015 16:36:25 +0200 Subject: [PATCH 04/79] First draft of group blueprints --- system/blueprints/user/group.yaml | 8 ++++++++ system/blueprints/user/group_new.yaml | 16 ++++++++++++++++ 2 files changed, 24 insertions(+) create mode 100644 system/blueprints/user/group.yaml create mode 100644 system/blueprints/user/group_new.yaml diff --git a/system/blueprints/user/group.yaml b/system/blueprints/user/group.yaml new file mode 100644 index 000000000..31d6c9100 --- /dev/null +++ b/system/blueprints/user/group.yaml @@ -0,0 +1,8 @@ +title: Group +form: + validation: loose + fields: + + content: + type: section + title: PLUGIN_ADMIN_PRO.GROUP diff --git a/system/blueprints/user/group_new.yaml b/system/blueprints/user/group_new.yaml new file mode 100644 index 000000000..dd816a954 --- /dev/null +++ b/system/blueprints/user/group_new.yaml @@ -0,0 +1,16 @@ +title: PLUGIN_ADMIN_PRO.ADD_GROUP + +form: + validation: loose + fields: + + content: + type: section + title: PLUGIN_ADMIN_PRO.ADD_GROUP + + groupname: + type: text + label: PLUGIN_ADMIN_PRO.GROUP_NAME + help: PLUGIN_ADMIN_PRO.GROUP_NAME_HELP + validate: + required: true From 3e0188e40b148ef6d58d457e36d8d2c950e4cd2e Mon Sep 17 00:00:00 2001 From: Flavio Copes Date: Tue, 20 Oct 2015 19:31:25 +0200 Subject: [PATCH 05/79] Group blueprint --- system/blueprints/user/group.yaml | 42 ++++++++++++++++++++++++++++--- 1 file changed, 39 insertions(+), 3 deletions(-) diff --git a/system/blueprints/user/group.yaml b/system/blueprints/user/group.yaml index 31d6c9100..27c144e09 100644 --- a/system/blueprints/user/group.yaml +++ b/system/blueprints/user/group.yaml @@ -1,8 +1,44 @@ title: Group form: validation: loose + fields: + spacer: + type: spacer + text: '
' + + groupname: + type: text + size: large + label: PLUGIN_ADMIN.NAME + disabled: true + readonly: true + + readableName: + type: text + size: large + label: PLUGIN_ADMIN_PRO.READABLE_NAME + + description: + type: text + size: large + label: PLUGIN_ADMIN.DESCRIPTION + + icon: + type: text + size: small + label: PLUGIN_ADMIN_PRO.ICON + + access.admin: + type: array + label: PLUGIN_ADMIN_PRO.ADMIN_ACCESS + multiple: false + validate: + type: array - content: - type: section - title: PLUGIN_ADMIN_PRO.GROUP + access.site: + type: array + label: PLUGIN_ADMIN_PRO.SITE_ACCESS + multiple: false + validate: + type: array \ No newline at end of file From 14347ebf8828560a0ed45c6d257196d452d1b675 Mon Sep 17 00:00:00 2001 From: Flavio Copes Date: Tue, 20 Oct 2015 19:31:47 +0200 Subject: [PATCH 06/79] Group object --- system/src/Grav/Common/User/Group.php | 105 ++++++++++++++++++++++++++ 1 file changed, 105 insertions(+) create mode 100644 system/src/Grav/Common/User/Group.php diff --git a/system/src/Grav/Common/User/Group.php b/system/src/Grav/Common/User/Group.php new file mode 100644 index 000000000..e71370822 --- /dev/null +++ b/system/src/Grav/Common/User/Group.php @@ -0,0 +1,105 @@ +get('site.groups'); + return $groups; + } + + /** + * Get a group by name + * + * @return object + */ + public static function load($groupname) + { + $content = self::groups()[$groupname]; + + $blueprints = new Blueprints('blueprints://'); + $blueprint = $blueprints->get('user/group'); + if (!isset($content['groupname'])) { + $content['groupname'] = $groupname; + } + $group = new Group($content, $blueprint); + + return $group; + } + + /** + * Get value of an array using dot notation + */ + private function resolve(array $array, $path, $default = null) + { + $current = $array; + $p = strtok($path, '.'); + + while ($p !== false) { + if (!isset($current[$p])) { + return $default; + } + $current = $current[$p]; + $p = strtok('.'); + } + + return $current; + } + + /** + * Save a group + */ + public function save() + { + $blueprints = new Blueprints('blueprints://'); + $blueprint = $blueprints->get('user/group'); + + $fields = $blueprint->fields(); + + self::getGrav()['config']->set("site.groups.$this->groupname", []); + + foreach($fields as $field) { + if ($field['type'] == 'text') { + $value = $field['name']; + self::getGrav()['config']->set("site.groups.$this->groupname.$value", $this->items[$field['name']]); + } + if ($field['type'] == 'array') { + $value = $field['name']; + $arrayValues = $this->resolve($this->items, $field['name']); + + if ($arrayValues) foreach($arrayValues as $arrayIndex => $arrayValue) { + self::getGrav()['config']->set("site.groups.$this->groupname.$value.$arrayIndex", $arrayValue); + } + } + } + + $type = 'site'; + $blueprints = $this->blueprints("config/{$type}"); + $obj = new Data(self::getGrav()['config']->get('site'), $blueprints); + $file = CompiledYamlFile::instance(self::getGrav()['locator']->findResource("config://{$type}.yaml")); + $obj->file($file); + $obj->save(); + } +} From fb500d3e1cc0cbced1d6c01d28ca5d6e8fd3db71 Mon Sep 17 00:00:00 2001 From: Flavio Copes Date: Wed, 21 Oct 2015 11:09:29 +0200 Subject: [PATCH 07/79] Handle saving a new group --- system/src/Grav/Common/User/Group.php | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/system/src/Grav/Common/User/Group.php b/system/src/Grav/Common/User/Group.php index e71370822..907f2ac40 100644 --- a/system/src/Grav/Common/User/Group.php +++ b/system/src/Grav/Common/User/Group.php @@ -30,6 +30,16 @@ private static function groups() return $groups; } + /** + * Checks if a group exists + * + * @return object + */ + public static function group_exists($groupname) + { + return isset(self::groups()[$groupname]); + } + /** * Get a group by name * @@ -37,7 +47,11 @@ private static function groups() */ public static function load($groupname) { - $content = self::groups()[$groupname]; + if (self::group_exists($groupname)) { + $content = self::groups()[$groupname]; + } else { + $content = []; + } $blueprints = new Blueprints('blueprints://'); $blueprint = $blueprints->get('user/group'); @@ -83,7 +97,9 @@ public function save() foreach($fields as $field) { if ($field['type'] == 'text') { $value = $field['name']; - self::getGrav()['config']->set("site.groups.$this->groupname.$value", $this->items[$field['name']]); + if (isset($this->items[$value])) { + self::getGrav()['config']->set("site.groups.$this->groupname.$value", $this->items[$value]); + } } if ($field['type'] == 'array') { $value = $field['name']; From f1d4192ae7c72919f21d8379abf7acaa9f14b3cf Mon Sep 17 00:00:00 2001 From: Flavio Copes Date: Wed, 21 Oct 2015 12:20:45 +0200 Subject: [PATCH 08/79] Introduce a resolve() function to Utils, used by Group and User to access an array using dot notation --- system/src/Grav/Common/Utils.php | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/system/src/Grav/Common/Utils.php b/system/src/Grav/Common/Utils.php index c0b15af44..9f21c315c 100644 --- a/system/src/Grav/Common/Utils.php +++ b/system/src/Grav/Common/Utils.php @@ -377,4 +377,23 @@ public static function date2timestamp($date) } } + /** + * Get value of an array using dot notation + */ + public static function resolve(array $array, $path, $default = null) + { + $current = $array; + $p = strtok($path, '.'); + + while ($p !== false) { + if (!isset($current[$p])) { + return $default; + } + $current = $current[$p]; + $p = strtok('.'); + } + + return $current; + } + } From f973b61b5ed89fdcb198994c7a4092044a686863 Mon Sep 17 00:00:00 2001 From: Flavio Copes Date: Wed, 21 Oct 2015 12:21:23 +0200 Subject: [PATCH 09/79] Change how permissions work: if true in group, but false in user, false has precedence (allows to fine tune removing permissions per-user) --- system/src/Grav/Common/User/User.php | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/system/src/Grav/Common/User/User.php b/system/src/Grav/Common/User/User.php index a14bfdb1d..24a734335 100644 --- a/system/src/Grav/Common/User/User.php +++ b/system/src/Grav/Common/User/User.php @@ -5,6 +5,7 @@ use Grav\Common\Data\Data; use Grav\Common\File\CompiledYamlFile; use Grav\Common\GravTrait; +use Grav\Common\Utils; /** * User object @@ -138,16 +139,22 @@ public function authorize($action) return false; } + $return = false; + //Check group access level $groups = $this->get('groups'); if ($groups) foreach($groups as $group) { if (self::getGrav()['config']->get("site.groups.{$group}.access.{$action}") === true) { - return true; + $return = true; } } - //Fallback to user access level - return $this->get("access.{$action}") === true; + //Check user access level + if (Utils::resolve($this->get("access"), $action) !== null) { + $return = $this->get("access.{$action}"); + } + + return $return; } /** From f95a4f5cc6a287f46b5149a9fda5b828691dd8a1 Mon Sep 17 00:00:00 2001 From: Flavio Copes Date: Wed, 21 Oct 2015 12:21:47 +0200 Subject: [PATCH 10/79] Move resolve() to Utils. --- system/src/Grav/Common/User/Group.php | 22 ++-------------------- 1 file changed, 2 insertions(+), 20 deletions(-) diff --git a/system/src/Grav/Common/User/Group.php b/system/src/Grav/Common/User/Group.php index 907f2ac40..fcd890f2b 100644 --- a/system/src/Grav/Common/User/Group.php +++ b/system/src/Grav/Common/User/Group.php @@ -5,6 +5,7 @@ use Grav\Common\Data\Data; use Grav\Common\File\CompiledYamlFile; use Grav\Common\GravTrait; +use Grav\Common\Utils; /** * Group object @@ -63,25 +64,6 @@ public static function load($groupname) return $group; } - /** - * Get value of an array using dot notation - */ - private function resolve(array $array, $path, $default = null) - { - $current = $array; - $p = strtok($path, '.'); - - while ($p !== false) { - if (!isset($current[$p])) { - return $default; - } - $current = $current[$p]; - $p = strtok('.'); - } - - return $current; - } - /** * Save a group */ @@ -103,7 +85,7 @@ public function save() } if ($field['type'] == 'array') { $value = $field['name']; - $arrayValues = $this->resolve($this->items, $field['name']); + $arrayValues = Utils::resolve($this->items, $field['name']); if ($arrayValues) foreach($arrayValues as $arrayIndex => $arrayValue) { self::getGrav()['config']->set("site.groups.$this->groupname.$value.$arrayIndex", $arrayValue); From fe7873ddbee98cb7a9ffcf6ea44f79cd0d00d9f3 Mon Sep 17 00:00:00 2001 From: Flavio Copes Date: Wed, 21 Oct 2015 12:21:53 +0200 Subject: [PATCH 11/79] Allow to remove a group --- system/src/Grav/Common/User/Group.php | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/system/src/Grav/Common/User/Group.php b/system/src/Grav/Common/User/Group.php index fcd890f2b..c0e59fc25 100644 --- a/system/src/Grav/Common/User/Group.php +++ b/system/src/Grav/Common/User/Group.php @@ -95,9 +95,33 @@ public function save() $type = 'site'; $blueprints = $this->blueprints("config/{$type}"); - $obj = new Data(self::getGrav()['config']->get('site'), $blueprints); + $obj = new Data(self::getGrav()['config']->get($type), $blueprints); $file = CompiledYamlFile::instance(self::getGrav()['locator']->findResource("config://{$type}.yaml")); $obj->file($file); $obj->save(); } + + /** + * Remove a group + * + * @param string $username + * @return bool True is the action was performed + */ + public static function remove($groupname) + { + $blueprints = new Blueprints('blueprints://'); + $blueprint = $blueprints->get('user/group'); + + $groups = self::getGrav()['config']->get("site.groups"); + unset($groups[$groupname]); + self::getGrav()['config']->set("site.groups", $groups); + + $type = 'site'; + $obj = new Data(self::getGrav()['config']->get($type), $blueprint); + $file = CompiledYamlFile::instance(self::getGrav()['locator']->findResource("config://{$type}.yaml")); + $obj->file($file); + $obj->save(); + + return true; + } } From 4d33eb21733cca8a714e4fb260b8da01b5f74252 Mon Sep 17 00:00:00 2001 From: Flavio Copes Date: Wed, 21 Oct 2015 12:22:04 +0200 Subject: [PATCH 12/79] Add access to account blueprint --- system/blueprints/user/account.yaml | 13 +++++++++++++ system/blueprints/user/group.yaml | 4 ++-- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/system/blueprints/user/account.yaml b/system/blueprints/user/account.yaml index 207421ae0..a1e23cc3a 100644 --- a/system/blueprints/user/account.yaml +++ b/system/blueprints/user/account.yaml @@ -54,3 +54,16 @@ form: default: 'en' help: PLUGIN_ADMIN.LANGUAGE_HELP + 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/blueprints/user/group.yaml b/system/blueprints/user/group.yaml index 27c144e09..e627a7c9e 100644 --- a/system/blueprints/user/group.yaml +++ b/system/blueprints/user/group.yaml @@ -31,14 +31,14 @@ form: access.admin: type: array - label: PLUGIN_ADMIN_PRO.ADMIN_ACCESS + label: PLUGIN_ADMIN.ADMIN_ACCESS multiple: false validate: type: array access.site: type: array - label: PLUGIN_ADMIN_PRO.SITE_ACCESS + label: PLUGIN_ADMIN.SITE_ACCESS multiple: false validate: type: array \ No newline at end of file From 5f11ae74824e528a7ca9608d26aaf7515f66b0b9 Mon Sep 17 00:00:00 2001 From: Flavio Copes Date: Wed, 21 Oct 2015 12:28:36 +0200 Subject: [PATCH 13/79] Allow editing user groups from user form --- system/blueprints/user/account.yaml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/system/blueprints/user/account.yaml b/system/blueprints/user/account.yaml index a1e23cc3a..5ccd5f641 100644 --- a/system/blueprints/user/account.yaml +++ b/system/blueprints/user/account.yaml @@ -54,6 +54,16 @@ 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 + access.admin: type: array label: PLUGIN_ADMIN.ADMIN_ACCESS From ae17a777898bb78adc3bc777f9985e8a9e27723f Mon Sep 17 00:00:00 2001 From: Flavio Copes Date: Wed, 21 Oct 2015 17:47:39 +0200 Subject: [PATCH 14/79] Use config/groups.yaml instead of config/site.yaml --- system/src/Grav/Common/User/Group.php | 16 ++++++++-------- system/src/Grav/Common/User/User.php | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/system/src/Grav/Common/User/Group.php b/system/src/Grav/Common/User/Group.php index c0e59fc25..cbf764337 100644 --- a/system/src/Grav/Common/User/Group.php +++ b/system/src/Grav/Common/User/Group.php @@ -27,7 +27,7 @@ class Group extends Data */ private static function groups() { - $groups = self::getGrav()['config']->get('site.groups'); + $groups = self::getGrav()['config']->get('groups'); return $groups; } @@ -74,13 +74,13 @@ public function save() $fields = $blueprint->fields(); - self::getGrav()['config']->set("site.groups.$this->groupname", []); + self::getGrav()['config']->set("groups.$this->groupname", []); foreach($fields as $field) { if ($field['type'] == 'text') { $value = $field['name']; if (isset($this->items[$value])) { - self::getGrav()['config']->set("site.groups.$this->groupname.$value", $this->items[$value]); + self::getGrav()['config']->set("groups.$this->groupname.$value", $this->items[$value]); } } if ($field['type'] == 'array') { @@ -88,12 +88,12 @@ public function save() $arrayValues = Utils::resolve($this->items, $field['name']); if ($arrayValues) foreach($arrayValues as $arrayIndex => $arrayValue) { - self::getGrav()['config']->set("site.groups.$this->groupname.$value.$arrayIndex", $arrayValue); + self::getGrav()['config']->set("groups.$this->groupname.$value.$arrayIndex", $arrayValue); } } } - $type = 'site'; + $type = 'groups'; $blueprints = $this->blueprints("config/{$type}"); $obj = new Data(self::getGrav()['config']->get($type), $blueprints); $file = CompiledYamlFile::instance(self::getGrav()['locator']->findResource("config://{$type}.yaml")); @@ -112,11 +112,11 @@ public static function remove($groupname) $blueprints = new Blueprints('blueprints://'); $blueprint = $blueprints->get('user/group'); - $groups = self::getGrav()['config']->get("site.groups"); + $groups = self::getGrav()['config']->get("groups"); unset($groups[$groupname]); - self::getGrav()['config']->set("site.groups", $groups); + self::getGrav()['config']->set("groups", $groups); - $type = 'site'; + $type = 'groups'; $obj = new Data(self::getGrav()['config']->get($type), $blueprint); $file = CompiledYamlFile::instance(self::getGrav()['locator']->findResource("config://{$type}.yaml")); $obj->file($file); diff --git a/system/src/Grav/Common/User/User.php b/system/src/Grav/Common/User/User.php index 24a734335..bcab5a397 100644 --- a/system/src/Grav/Common/User/User.php +++ b/system/src/Grav/Common/User/User.php @@ -144,7 +144,7 @@ public function authorize($action) //Check group access level $groups = $this->get('groups'); if ($groups) foreach($groups as $group) { - if (self::getGrav()['config']->get("site.groups.{$group}.access.{$action}") === true) { + if (self::getGrav()['config']->get("groups.{$group}.access.{$action}") === true) { $return = true; } } From 0ff5dc0016a0cb9eb612098a6de640bf778b38c2 Mon Sep 17 00:00:00 2001 From: Flavio Copes Date: Wed, 21 Oct 2015 18:41:34 +0200 Subject: [PATCH 15/79] Add accessLevels to return the used page access levels --- system/src/Grav/Common/Page/Pages.php | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/system/src/Grav/Common/Page/Pages.php b/system/src/Grav/Common/Page/Pages.php index 20a24f912..701e9dc73 100644 --- a/system/src/Grav/Common/Page/Pages.php +++ b/system/src/Grav/Common/Page/Pages.php @@ -480,6 +480,29 @@ public static function pageTypes() return static::types(); } + /** + * Get access levels of the site pages + * + * @return array + */ + public function accessLevels() + { + $accessLevels = []; + foreach($this->all() as $page) { + if (isset($page->header()->access)) { + if (is_array($page->header()->access)) { + foreach($page->header()->access as $index => $accessLevel) { + array_push($accessLevels, $index); + } + } else { + array_push($accessLevels, $page->header()->access); + } + } + } + + return array_unique($accessLevels); + } + /** * Get available parents. * From 67fefb53ada43656155c6c5f5faf0c573edad79c Mon Sep 17 00:00:00 2001 From: Flavio Copes Date: Wed, 21 Oct 2015 19:47:06 +0200 Subject: [PATCH 16/79] Allow to filter pages by access level --- system/src/Grav/Common/Page/Collection.php | 49 ++++++++++++++++++++++ system/src/Grav/Common/Page/Pages.php | 9 +++- 2 files changed, 57 insertions(+), 1 deletion(-) diff --git a/system/src/Grav/Common/Page/Collection.php b/system/src/Grav/Common/Page/Collection.php index 5abb8e939..4e85b419f 100644 --- a/system/src/Grav/Common/Page/Collection.php +++ b/system/src/Grav/Common/Page/Collection.php @@ -439,6 +439,55 @@ public function ofOneOfTheseTypes($types) return $this; } + /** + * Creates new collection with only pages of one of the specified access levels + * + * @return Collection The collection + */ + public function ofOneOfTheseAccessLevels($accessLevels) + { + $items = []; + + foreach ($this->items as $path => $slug) { + $page = $this->pages->get($path); + + if ($page !== null && isset($page->header()->access)) { + if (is_array($page->header()->access)) { + //Multiple values for access + $valid = false; + + foreach ($page->header()->access as $index => $accessLevel) { + if (is_array($accessLevel)) { + foreach($accessLevel as $innerIndex => $innerAccessLevel) { + if (in_array($innerAccessLevel, $accessLevels)) { + $valid = true; + } + } + } else { + if (in_array($index, $accessLevels)) { + $valid = true; + } + } + } + if ($valid) { + $items[$path] = $slug; + } + } else { + //Single value for access + if (in_array($page->header()->access, $accessLevels)) { + $items[$path] = $slug; + } + } + + } + } + + $this->items = $items; + return $this; + } + + + } diff --git a/system/src/Grav/Common/Page/Pages.php b/system/src/Grav/Common/Page/Pages.php index 701e9dc73..b238c119c 100644 --- a/system/src/Grav/Common/Page/Pages.php +++ b/system/src/Grav/Common/Page/Pages.php @@ -492,9 +492,16 @@ public function accessLevels() if (isset($page->header()->access)) { if (is_array($page->header()->access)) { foreach($page->header()->access as $index => $accessLevel) { - array_push($accessLevels, $index); + if (is_array($accessLevel)) { + foreach($accessLevel as $innerIndex => $innerAccessLevel) { + array_push($accessLevels, $innerIndex); + } + } else { + array_push($accessLevels, $index); + } } } else { + array_push($accessLevels, $page->header()->access); } } From 87378562ea46721c5208981f1db8b64d69735d5f Mon Sep 17 00:00:00 2001 From: Flavio Copes Date: Tue, 3 Nov 2015 14:35:11 +0100 Subject: [PATCH 17/79] Fix error when logging in with a non-existing username --- system/src/Grav/Common/User/User.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/system/src/Grav/Common/User/User.php b/system/src/Grav/Common/User/User.php index 72cd41c3f..5b12e89d8 100644 --- a/system/src/Grav/Common/User/User.php +++ b/system/src/Grav/Common/User/User.php @@ -151,7 +151,11 @@ public function authorize($action) } //Check user access level - if (Utils::resolve($this->get("access"), $action) !== null) { + if (!$this->get('access')) { + return false; + } + + if (Utils::resolve($this->get('access'), $action) !== null) { $permission = $this->get("access.{$action}"); $return = Utils::isPositive($permission); } From 76e01e7aeaa70b326e03e27b32aba5b365085dad Mon Sep 17 00:00:00 2001 From: Flavio Copes Date: Wed, 4 Nov 2015 14:44:21 +0100 Subject: [PATCH 18/79] Minor fix --- system/src/Grav/Common/User/Group.php | 2 +- system/src/Grav/Common/User/User.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/system/src/Grav/Common/User/Group.php b/system/src/Grav/Common/User/Group.php index cbf764337..965fde5f6 100644 --- a/system/src/Grav/Common/User/Group.php +++ b/system/src/Grav/Common/User/Group.php @@ -105,7 +105,7 @@ public function save() * Remove a group * * @param string $username - * @return bool True is the action was performed + * @return bool True if the action was performed */ public static function remove($groupname) { diff --git a/system/src/Grav/Common/User/User.php b/system/src/Grav/Common/User/User.php index 5b12e89d8..5ea2f78cc 100644 --- a/system/src/Grav/Common/User/User.php +++ b/system/src/Grav/Common/User/User.php @@ -50,7 +50,7 @@ public static function load($username) * Remove user account. * * @param string $username - * @return bool True is the action was performed + * @return bool True if the action was performed */ public static function remove($username) { From 6fdfaccc92075b21f293e0f42718711b4c0f396e Mon Sep 17 00:00:00 2001 From: Flavio Copes Date: Wed, 4 Nov 2015 14:54:47 +0100 Subject: [PATCH 19/79] Add blueprints --- system/blueprints/media/move.yaml | 8 ++++++++ system/blueprints/media/rename.yaml | 8 ++++++++ 2 files changed, 16 insertions(+) create mode 100644 system/blueprints/media/move.yaml create mode 100644 system/blueprints/media/rename.yaml diff --git a/system/blueprints/media/move.yaml b/system/blueprints/media/move.yaml new file mode 100644 index 000000000..6287e0595 --- /dev/null +++ b/system/blueprints/media/move.yaml @@ -0,0 +1,8 @@ +form: + validation: loose + fields: + route: + type: select + label: PLUGIN_ADMIN.PAGE + classes: fancy + '@data-options': '\Grav\Common\Page\Pages::parents' diff --git a/system/blueprints/media/rename.yaml b/system/blueprints/media/rename.yaml new file mode 100644 index 000000000..529349cac --- /dev/null +++ b/system/blueprints/media/rename.yaml @@ -0,0 +1,8 @@ +form: + validation: loose + fields: + new_file_name: + type: text + label: PLUGIN_ADMIN_PRO.NEW_FILE_NAME + validate: + required: true From bc4a09f80dac10646a3278690e33b66ca5a98882 Mon Sep 17 00:00:00 2001 From: Matias Griese Date: Fri, 13 Nov 2015 14:02:06 +0200 Subject: [PATCH 20/79] Fix undefined variable in Config class --- CHANGELOG.md | 6 ++ composer.json | 8 ++- composer.lock | 43 ++++++------ system/src/Grav/Common/Data/Blueprint.php | 3 +- system/src/Grav/Common/Data/Data.php | 4 +- .../src/Grav/Common/Data/DataMutatorTrait.php | 68 ------------------- 6 files changed, 41 insertions(+), 91 deletions(-) delete mode 100644 system/src/Grav/Common/Data/DataMutatorTrait.php diff --git a/CHANGELOG.md b/CHANGELOG.md index 4300bc74b..5fd5b48fa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +# v1.0.0-refactor +## XX/XX/2015 + +1. [](#new) + * Refactor Data classes to use NestedArrayAccess instead of DataMutatorTrait + # v1.0.0-rc.4 ## 10/29/2015 diff --git a/composer.json b/composer.json index 724cff2f8..a4a62a98e 100644 --- a/composer.json +++ b/composer.json @@ -21,9 +21,15 @@ "mrclay/minify": "~2.2", "donatj/phpuseragentparser": "~0.3", "pimple/pimple": "~3.0", - "rockettheme/toolbox": "1.1.*", + "rockettheme/toolbox": "dev-develop", "maximebf/debugbar": "~1.10" }, + "repositories": [ + { + "type": "vcs", + "url": "https://github.com/rockettheme/toolbox" + } + ], "autoload": { "psr-4": { "Grav\\": "system/src/Grav" diff --git a/composer.lock b/composer.lock index d3a9d25b8..9328557aa 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": "e1db721096772d41f16003b39b47c85a", + "hash": "3633e92a6e340a26229689d9b74031c2", + "content-hash": "9c314f997d29d7a78d3cf14f5976e6df", "packages": [ { "name": "doctrine/cache", - "version": "v1.5.0", + "version": "v1.5.1", "source": { "type": "git", "url": "https://github.com/doctrine/cache.git", - "reference": "eb8a73619af4f1c8711e2ce482f5de3643258a1f" + "reference": "2b9cec5a5e722010cbebc91713d4c11eaa064d5e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/cache/zipball/eb8a73619af4f1c8711e2ce482f5de3643258a1f", - "reference": "eb8a73619af4f1c8711e2ce482f5de3643258a1f", + "url": "https://api.github.com/repos/doctrine/cache/zipball/2b9cec5a5e722010cbebc91713d4c11eaa064d5e", + "reference": "2b9cec5a5e722010cbebc91713d4c11eaa064d5e", "shasum": "" }, "require": { @@ -74,7 +75,7 @@ "cache", "caching" ], - "time": "2015-10-28 11:27:45" + "time": "2015-11-02 18:35:48" }, { "name": "donatj/phpuseragentparser", @@ -665,16 +666,16 @@ }, { "name": "rockettheme/toolbox", - "version": "1.1.4", + "version": "dev-develop", "source": { "type": "git", "url": "https://github.com/rockettheme/toolbox.git", - "reference": "ff677d8f66d1addd3590d0cb85bcbaff4174d9c9" + "reference": "0a7006b00880bd6873c49e2ed23939c785310417" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/rockettheme/toolbox/zipball/ff677d8f66d1addd3590d0cb85bcbaff4174d9c9", - "reference": "ff677d8f66d1addd3590d0cb85bcbaff4174d9c9", + "url": "https://api.github.com/repos/rockettheme/toolbox/zipball/0a7006b00880bd6873c49e2ed23939c785310417", + "reference": "0a7006b00880bd6873c49e2ed23939c785310417", "shasum": "" }, "require": { @@ -700,7 +701,6 @@ "RocketTheme\\Toolbox\\StreamWrapper\\": "StreamWrapper/src" } }, - "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], @@ -710,7 +710,11 @@ "php", "rockettheme" ], - "time": "2015-10-15 23:27:40" + "support": { + "source": "https://github.com/rockettheme/toolbox/tree/develop", + "issues": "https://github.com/rockettheme/toolbox/issues" + }, + "time": "2015-11-13 11:39:58" }, { "name": "symfony/console", @@ -929,16 +933,16 @@ }, { "name": "twig/twig", - "version": "v1.23.0", + "version": "v1.23.1", "source": { "type": "git", "url": "https://github.com/twigphp/Twig.git", - "reference": "5868cd822fd6cf626d5f805439575f9c323cee2a" + "reference": "d9b6333ae8dd2c8e3fd256e127548def0bc614c6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/twigphp/Twig/zipball/5868cd822fd6cf626d5f805439575f9c323cee2a", - "reference": "5868cd822fd6cf626d5f805439575f9c323cee2a", + "url": "https://api.github.com/repos/twigphp/Twig/zipball/d9b6333ae8dd2c8e3fd256e127548def0bc614c6", + "reference": "d9b6333ae8dd2c8e3fd256e127548def0bc614c6", "shasum": "" }, "require": { @@ -986,14 +990,15 @@ "keywords": [ "templating" ], - "time": "2015-10-29 23:29:01" + "time": "2015-11-05 12:49:06" } ], "packages-dev": [], "aliases": [], "minimum-stability": "stable", "stability-flags": { - "filp/whoops": 20 + "filp/whoops": 20, + "rockettheme/toolbox": 20 }, "prefer-stable": false, "prefer-lowest": false, diff --git a/system/src/Grav/Common/Data/Blueprint.php b/system/src/Grav/Common/Data/Blueprint.php index 97ed5858a..defb978b3 100644 --- a/system/src/Grav/Common/Data/Blueprint.php +++ b/system/src/Grav/Common/Data/Blueprint.php @@ -3,6 +3,7 @@ use Grav\Common\GravTrait; use RocketTheme\Toolbox\ArrayTraits\Export; +use RocketTheme\Toolbox\ArrayTraits\NestedArrayAccess; /** * Blueprint handles the inside logic of blueprints. @@ -12,7 +13,7 @@ */ class Blueprint { - use Export, DataMutatorTrait, GravTrait; + use Export, NestedArrayAccess, GravTrait; public $name; diff --git a/system/src/Grav/Common/Data/Data.php b/system/src/Grav/Common/Data/Data.php index 6a35fbc9e..e09611478 100644 --- a/system/src/Grav/Common/Data/Data.php +++ b/system/src/Grav/Common/Data/Data.php @@ -1,9 +1,9 @@ get('this.is.my.nested.variable'); - * - * @param string $name Dot separated path to the requested value. - * @param mixed $default Default value (or null). - * @param string $separator Separator, defaults to '.' - * @return mixed Value. - */ - public function get($name, $default = null, $separator = '.') - { - $path = explode($separator, $name); - $current = $this->items; - foreach ($path as $field) { - if (is_object($current) && isset($current->{$field})) { - $current = $current->{$field}; - } elseif (is_array($current) && isset($current[$field])) { - $current = $current[$field]; - } else { - return $default; - } - } - - return $current; - } - - /** - * Set value by using dot notation for nested arrays/objects. - * - * @example $value = $data->set('this.is.my.nested.variable', true); - * - * @param string $name Dot separated path to the requested value. - * @param mixed $value New value. - * @param string $separator Separator, defaults to '.' - */ - public function set($name, $value, $separator = '.') - { - $path = explode($separator, $name); - $current = &$this->items; - foreach ($path as $field) { - if (is_object($current)) { - // Handle objects. - if (!isset($current->{$field})) { - $current->{$field} = array(); - } - $current = &$current->{$field}; - } else { - // Handle arrays and scalars. - if (!is_array($current)) { - $current = array($field => array()); - } elseif (!isset($current[$field])) { - $current[$field] = array(); - } - $current = &$current[$field]; - } - } - - $current = $value; - } - -} From 997c772b7c469bd990b09335028f61e02550671a Mon Sep 17 00:00:00 2001 From: Matias Griese Date: Fri, 13 Nov 2015 14:09:03 +0200 Subject: [PATCH 21/79] Make Data classes to implement proper interfaces --- system/src/Grav/Common/Data/Blueprint.php | 7 ++++--- system/src/Grav/Common/Data/Data.php | 3 ++- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/system/src/Grav/Common/Data/Blueprint.php b/system/src/Grav/Common/Data/Blueprint.php index defb978b3..6ebe8ae19 100644 --- a/system/src/Grav/Common/Data/Blueprint.php +++ b/system/src/Grav/Common/Data/Blueprint.php @@ -3,7 +3,8 @@ use Grav\Common\GravTrait; use RocketTheme\Toolbox\ArrayTraits\Export; -use RocketTheme\Toolbox\ArrayTraits\NestedArrayAccess; +use RocketTheme\Toolbox\ArrayTraits\ExportInterface; +use RocketTheme\Toolbox\ArrayTraits\NestedArrayAccessWithGetters; /** * Blueprint handles the inside logic of blueprints. @@ -11,9 +12,9 @@ * @author RocketTheme * @license MIT */ -class Blueprint +class Blueprint implements \ArrayAccess, ExportInterface { - use Export, NestedArrayAccess, GravTrait; + use Export, NestedArrayAccessWithGetters, GravTrait; public $name; diff --git a/system/src/Grav/Common/Data/Data.php b/system/src/Grav/Common/Data/Data.php index e09611478..4e38f1eaa 100644 --- a/system/src/Grav/Common/Data/Data.php +++ b/system/src/Grav/Common/Data/Data.php @@ -3,6 +3,7 @@ use RocketTheme\Toolbox\ArrayTraits\Countable; use RocketTheme\Toolbox\ArrayTraits\Export; +use RocketTheme\Toolbox\ArrayTraits\ExportInterface; use RocketTheme\Toolbox\ArrayTraits\NestedArrayAccessWithGetters; use RocketTheme\Toolbox\File\File; use RocketTheme\Toolbox\File\FileInterface; @@ -13,7 +14,7 @@ * @author RocketTheme * @license MIT */ -class Data implements DataInterface +class Data implements DataInterface, \ArrayAccess, \Countable, ExportInterface { use NestedArrayAccessWithGetters, Countable, Export; From 184cb9ea3a546dcfadf75a92d5320b3687a6bbff Mon Sep 17 00:00:00 2001 From: Flavio Copes Date: Mon, 16 Nov 2015 20:15:19 +0100 Subject: [PATCH 22/79] Media Meta blueprint --- system/blueprints/media/meta.yaml | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 system/blueprints/media/meta.yaml diff --git a/system/blueprints/media/meta.yaml b/system/blueprints/media/meta.yaml new file mode 100644 index 000000000..7d242e0b5 --- /dev/null +++ b/system/blueprints/media/meta.yaml @@ -0,0 +1,7 @@ +form: + validation: loose + fields: + + alt_text: + type: string + label: Alt Text From d888dcd0852cc0d24d7067603b769fe93e344283 Mon Sep 17 00:00:00 2001 From: Flavio Copes Date: Mon, 16 Nov 2015 20:15:47 +0100 Subject: [PATCH 23/79] Add some methods to ImageMedium to be used by the media manager --- .../Grav/Common/Page/Medium/ImageMedium.php | 40 ++++++++++++++++--- 1 file changed, 34 insertions(+), 6 deletions(-) diff --git a/system/src/Grav/Common/Page/Medium/ImageMedium.php b/system/src/Grav/Common/Page/Medium/ImageMedium.php index c9c5dcc07..4c15adb9d 100644 --- a/system/src/Grav/Common/Page/Medium/ImageMedium.php +++ b/system/src/Grav/Common/Page/Medium/ImageMedium.php @@ -312,19 +312,23 @@ public function lightbox($width = null, $height = null, $reset = true) } /** - * Sets the quality of the image + * Sets or gets the quality of the image * * @param int $quality 0-100 quality * @return Medium */ - public function quality($quality) + public function quality($quality = null) { - if (!$this->image) { - $this->image(); + if ($quality) { + if (!$this->image) { + $this->image(); + } + + $this->quality = $quality; + return $this; } - $this->quality = $quality; - return $this; + return $this->quality; } /** @@ -513,4 +517,28 @@ public function filter($filter = 'image.filters.default') $this->__call($method, $params); } } + + /** + * Return the image higher quality version + * + * @return ImageMedium the alternative version with higher quality + */ + public function higherQualityAlternative() + { + if ($this->alternatives) { + $max = reset($this->alternatives); + foreach($this->alternatives as $alternative) + { + if($alternative->quality() > $max->quality()) + { + $max = $alternative; + } + } + + return $max; + } else { + return $this; + } + } + } From ac3396e6c46f9bcd8d5cbb45370b5daadc84aadc Mon Sep 17 00:00:00 2001 From: Matias Griese Date: Wed, 18 Nov 2015 14:43:32 +0200 Subject: [PATCH 24/79] Data objects: Allow function call chaining, lazy load blueprints --- CHANGELOG.md | 2 + system/src/Grav/Common/Data/Data.php | 161 +++++++++++++++++---------- 2 files changed, 103 insertions(+), 60 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5fd5b48fa..8590da83a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,8 @@ 1. [](#new) * Refactor Data classes to use NestedArrayAccess instead of DataMutatorTrait + * Data objects: Allow function call chaining + * Data objects: Lazy load blueprints only if needed # v1.0.0-rc.4 ## 10/29/2015 diff --git a/system/src/Grav/Common/Data/Data.php b/system/src/Grav/Common/Data/Data.php index 4e38f1eaa..fe70efb1b 100644 --- a/system/src/Grav/Common/Data/Data.php +++ b/system/src/Grav/Common/Data/Data.php @@ -5,6 +5,7 @@ use RocketTheme\Toolbox\ArrayTraits\Export; use RocketTheme\Toolbox\ArrayTraits\ExportInterface; use RocketTheme\Toolbox\ArrayTraits\NestedArrayAccessWithGetters; +use RocketTheme\Toolbox\Blueprints\Blueprints; use RocketTheme\Toolbox\File\File; use RocketTheme\Toolbox\File\FileInterface; @@ -33,12 +34,11 @@ class Data implements DataInterface, \ArrayAccess, \Countable, ExportInterface /** * @param array $items - * @param Blueprint $blueprints + * @param Blueprint|callable $blueprints */ - public function __construct(array $items = array(), Blueprint $blueprints = null) + public function __construct(array $items = array(), $blueprints = null) { $this->items = $items; - $this->blueprints = $blueprints; } @@ -58,126 +58,150 @@ public function value($name, $default = null, $separator = '.') } /** - * Set default value by using dot notation for nested arrays/objects. - * - * @example $data->def('this.is.my.nested.variable', 'default'); + * Join nested values together by using blueprints. * * @param string $name Dot separated path to the requested value. - * @param mixed $default Default value (or null). + * @param mixed $value Value to be joined. * @param string $separator Separator, defaults to '.' + * @return $this + * @throws \RuntimeException */ - public function def($name, $default = null, $separator = '.') + public function join($name, $value, $separator = '.') { - $this->set($name, $this->get($name, $default, $separator), $separator); + $old = $this->get($name, null, $separator); + if ($old !== null) { + if (!is_array($old)) { + throw new \RuntimeException('Value ' . $old); + } + if (is_object($value)) { + $value = (array) $value; + } elseif (!is_array($value)) { + throw new \RuntimeException('Value ' . $value); + } + $value = $this->blueprints()->mergeData($old, $value, $name, $separator); + } + + $this->set($name, $value, $separator); + + return $this; } /** - * Join two values together by using blueprints if available. + * Get nested structure containing default values defined in the blueprints. + * + * Fields without default value are ignored in the list. + + * @return array + */ + public function getDefaults() + { + return $this->blueprints()->getDefaults(); + } + + /** + * Set default values by using blueprints. * * @param string $name Dot separated path to the requested value. * @param mixed $value Value to be joined. * @param string $separator Separator, defaults to '.' + * @return $this */ - public function join($name, $value, $separator = '.') + public function joinDefaults($name, $value, $separator = '.') { + if (is_object($value)) { + $value = (array) $value; + } $old = $this->get($name, null, $separator); - if ($old === null) { - // Variable does not exist yet: just use the incoming value. - } elseif ($this->blueprints) { - // Blueprints: join values by using blueprints. - $value = $this->blueprints->mergeData($old, $value, $name, $separator); - } else { - // No blueprints: replace existing top level variables with the new ones. - $value = array_merge($old, $value); + if ($old !== null) { + $value = $this->blueprints()->mergeData($value, $old, $name, $separator); } $this->set($name, $value, $separator); + + return $this; } /** - * Join two values together by using blueprints if available. + * Get value from the configuration and join it with given data. * * @param string $name Dot separated path to the requested value. - * @param mixed $value Value to be joined. + * @param array $value Value to be joined. * @param string $separator Separator, defaults to '.' + * @return array + * @throws \RuntimeException */ - public function joinDefaults($name, $value, $separator = '.') + public function getJoined($name, $value, $separator = '.') { + if (is_object($value)) { + $value = (array) $value; + } elseif (!is_array($value)) { + throw new \RuntimeException('Value ' . $value); + } + $old = $this->get($name, null, $separator); + if ($old === null) { - // Variable does not exist yet: just use the incoming value. - } elseif ($this->blueprints) { - // Blueprints: join values by using blueprints. - $value = $this->blueprints->mergeData($value, $old, $name, $separator); - } else { - // No blueprints: replace existing top level variables with the new ones. - $value = array_merge($value, $old); + // No value set; no need to join data. + return $value; } - $this->set($name, $value, $separator); + if (!is_array($old)) { + throw new \RuntimeException('Value ' . $old); + } + + // Return joined data. + return $this->blueprints()->mergeData($old, $value, $name, $separator); } /** - * Merge two sets of data together. + * Merge two configurations together. * * @param array $data - * @return void + * @return $this */ public function merge(array $data) { - if ($this->blueprints) { - $this->items = $this->blueprints->mergeData($this->items, $data); - } else { - $this->items = array_merge($this->items, $data); - } + $this->items = $this->blueprints()->mergeData($this->items, $data); + + return $this; } /** - * Add default data to the set. + * Set default values to the configuration if variables were not set. * * @param array $data - * @return void + * @return $this */ public function setDefaults(array $data) { - if ($this->blueprints) { - $this->items = $this->blueprints->mergeData($data, $this->items); - } else { - $this->items = array_merge($data, $this->items); - } - } + $this->items = $this->blueprints()->mergeData($data, $this->items); - /** - * Return blueprints. - * - * @return Blueprint - */ - public function blueprints() - { - return $this->blueprints; + return $this; } /** * Validate by blueprints. * + * @return $this * @throws \Exception */ public function validate() { - if ($this->blueprints) { - $this->blueprints->validate($this->items); - } + $this->blueprints()->validate($this->items); + + return $this; } /** + * @return $this * Filter all items by using blueprints. */ public function filter() { - if ($this->blueprints) { - $this->items = $this->blueprints->filter($this->items); - } + $this->items = $this->blueprints()->filter($this->items); + + return $this; } /** @@ -187,7 +211,24 @@ public function filter() */ public function extra() { - return $this->blueprints ? $this->blueprints->extra($this->items) : array(); + return $this->blueprints()->extra($this->items); + } + + /** + * Return blueprints. + * + * @return Blueprints + */ + public function blueprints() + { + if (!$this->blueprints){ + $this->blueprints = new Blueprints; + } elseif (is_callable($this->blueprints)) { + // Lazy load blueprints. + $blueprints = $this->blueprints; + $this->blueprints = $blueprints(); + } + return $this->blueprints; } /** From dd2ddfeb409baafc0bf7fed2a5c1f74b2bf8cae2 Mon Sep 17 00:00:00 2001 From: Matias Griese Date: Wed, 18 Nov 2015 15:14:27 +0200 Subject: [PATCH 25/79] Refactor Config classes --- CHANGELOG.md | 3 +- system/src/Grav/Common/Config/Blueprints.php | 207 -------- .../src/Grav/Common/Config/CompiledBase.php | 237 +++++++++ .../Grav/Common/Config/CompiledBlueprints.php | 44 ++ .../src/Grav/Common/Config/CompiledConfig.php | 88 ++++ .../Grav/Common/Config/CompiledLanguages.php | 48 ++ system/src/Grav/Common/Config/Config.php | 462 ++---------------- .../Grav/Common/Config/ConfigFileFinder.php | 258 ++++++++++ .../src/Grav/Common/Config/ConfigFinder.php | 186 ------- system/src/Grav/Common/Config/Languages.php | 9 + system/src/Grav/Common/Config/Setup.php | 232 +++++++++ .../Common/Service/ConfigServiceProvider.php | 105 +++- .../Common/Service/StreamsServiceProvider.php | 16 +- 13 files changed, 1033 insertions(+), 862 deletions(-) delete mode 100644 system/src/Grav/Common/Config/Blueprints.php create mode 100644 system/src/Grav/Common/Config/CompiledBase.php create mode 100644 system/src/Grav/Common/Config/CompiledBlueprints.php create mode 100644 system/src/Grav/Common/Config/CompiledConfig.php create mode 100644 system/src/Grav/Common/Config/CompiledLanguages.php create mode 100644 system/src/Grav/Common/Config/ConfigFileFinder.php delete mode 100644 system/src/Grav/Common/Config/ConfigFinder.php create mode 100644 system/src/Grav/Common/Config/Setup.php diff --git a/CHANGELOG.md b/CHANGELOG.md index 8590da83a..617449b80 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,8 @@ 1. [](#new) * Refactor Data classes to use NestedArrayAccess instead of DataMutatorTrait * Data objects: Allow function call chaining - * Data objects: Lazy load blueprints only if needed + * Data objects: Lazy load blueprints only if needed + * Refactor Config classes # v1.0.0-rc.4 ## 10/29/2015 diff --git a/system/src/Grav/Common/Config/Blueprints.php b/system/src/Grav/Common/Config/Blueprints.php deleted file mode 100644 index 1af61f47c..000000000 --- a/system/src/Grav/Common/Config/Blueprints.php +++ /dev/null @@ -1,207 +0,0 @@ -grav = $grav ?: Grav::instance(); - } - - public function init() - { - /** @var UniformResourceLocator $locator */ - $locator = $this->grav['locator']; - - $blueprints = $locator->findResources('blueprints://config'); - $plugins = $locator->findResources('plugins://'); - - $blueprintFiles = $this->getBlueprintFiles($blueprints, $plugins); - - $this->loadCompiledBlueprints($plugins + $blueprints, $blueprintFiles); - } - - protected function loadCompiledBlueprints($blueprints, $blueprintFiles) - { - $checksum = md5(serialize($blueprints)); - $filename = CACHE_DIR . 'compiled/blueprints/' . $checksum .'.php'; - $checksum .= ':'.md5(serialize($blueprintFiles)); - $class = get_class($this); - $file = PhpFile::instance($filename); - - if ($file->exists()) { - $cache = $file->exists() ? $file->content() : null; - } else { - $cache = null; - } - - - // Load real file if cache isn't up to date (or is invalid). - if ( - !is_array($cache) - || empty($cache['checksum']) - || empty($cache['$class']) - || $cache['checksum'] != $checksum - || $cache['@class'] != $class - ) { - // Attempt to lock the file for writing. - $file->lock(false); - - // Load blueprints. - $this->blueprints = new Blueprints(); - foreach ($blueprintFiles as $key => $files) { - $this->loadBlueprints($key); - } - - $cache = [ - '@class' => $class, - 'checksum' => $checksum, - 'files' => $blueprintFiles, - 'data' => $this->blueprints->toArray() - ]; - - // If compiled file wasn't already locked by another process, save it. - if ($file->locked() !== false) { - $file->save($cache); - $file->unlock(); - } - } else { - $this->blueprints = new Blueprints($cache['data']); - } - } - - /** - * Load global blueprints. - * - * @param string $key - * @param array $files - */ - public function loadBlueprints($key, array $files = null) - { - if (is_null($files)) { - $files = $this->files[$key]; - } - foreach ($files as $name => $item) { - $file = CompiledYamlFile::instance($item['file']); - $this->blueprints->embed($name, $file->content(), '/'); - } - } - - /** - * Get all blueprint files (including plugins). - * - * @param array $blueprints - * @param array $plugins - * @return array - */ - protected function getBlueprintFiles(array $blueprints, array $plugins) - { - $list = []; - foreach (array_reverse($plugins) as $folder) { - $list += $this->detectPlugins($folder, true); - } - foreach (array_reverse($blueprints) as $folder) { - $list += $this->detectConfig($folder, true); - } - return $list; - } - - /** - * Detects all plugins with a configuration file and returns last modification time. - * - * @param string $lookup Location to look up from. - * @param bool $blueprints - * @return array - * @internal - */ - protected function detectPlugins($lookup = SYSTEM_DIR, $blueprints = false) - { - $find = $blueprints ? 'blueprints.yaml' : '.yaml'; - $location = $blueprints ? 'blueprintFiles' : 'configFiles'; - $path = trim(Folder::getRelativePath($lookup), '/'); - if (isset($this->{$location}[$path])) { - return [$path => $this->{$location}[$path]]; - } - - $list = []; - - if (is_dir($lookup)) { - $iterator = new \DirectoryIterator($lookup); - - /** @var \DirectoryIterator $directory */ - foreach ($iterator as $directory) { - if (!$directory->isDir() || $directory->isDot()) { - continue; - } - - $name = $directory->getBasename(); - $filename = "{$path}/{$name}/" . ($find && $find[0] != '.' ? $find : $name . $find); - - if (is_file($filename)) { - $list["plugins/{$name}"] = ['file' => $filename, 'modified' => filemtime($filename)]; - } - } - } - - $this->{$location}[$path] = $list; - - return [$path => $list]; - } - - /** - * Detects all plugins with a configuration file and returns last modification time. - * - * @param string $lookup Location to look up from. - * @param bool $blueprints - * @return array - * @internal - */ - protected function detectConfig($lookup = SYSTEM_DIR, $blueprints = false) - { - $location = $blueprints ? 'blueprintFiles' : 'configFiles'; - $path = trim(Folder::getRelativePath($lookup), '/'); - if (isset($this->{$location}[$path])) { - return [$path => $this->{$location}[$path]]; - } - - if (is_dir($lookup)) { - // Find all system and user configuration files. - $options = [ - 'compare' => 'Filename', - 'pattern' => '|\.yaml$|', - 'filters' => [ - 'key' => '|\.yaml$|', - 'value' => function (\RecursiveDirectoryIterator $file) use ($path) { - return ['file' => "{$path}/{$file->getSubPathname()}", 'modified' => $file->getMTime()]; - }], - 'key' => 'SubPathname' - ]; - - $list = Folder::all($lookup, $options); - } else { - $list = []; - } - - $this->{$location}[$path] = $list; - - return [$path => $list]; - } -} diff --git a/system/src/Grav/Common/Config/CompiledBase.php b/system/src/Grav/Common/Config/CompiledBase.php new file mode 100644 index 000000000..d20223ef1 --- /dev/null +++ b/system/src/Grav/Common/Config/CompiledBase.php @@ -0,0 +1,237 @@ +cacheFolder = $cacheFolder; + $this->files = $files; + $this->path = $path ? rtrim($path, '\\/') . '/' : ''; + } + + /** + * Get filename for the compiled PHP file. + * + * @param string $name + * @return $this + */ + public function name($name = null) + { + if (!$this->name) { + $this->name = $name ?: md5(json_encode(array_keys($this->files))); + } + + return $this; + } + + /** + * @return bool + */ + public function modified() + { + return $this->modified; + } + + /** + * Load the configuration. + * + * @return mixed + */ + public function load() + { + if ($this->object) { + return $this->object; + } + + $filename = $this->createFilename(); + if (!$this->loadCompiledFile($filename) && $this->loadFiles()) { + $this->saveCompiledFile($filename); + } + + return $this->object; + } + + /** + * Returns checksum from the configuration files. + * + * You can set $this->checksum = false to disable this check. + * + * @return bool|string + */ + public function checksum() + { + if (!isset($this->checksum)) { + $this->checksum = md5(json_encode($this->files) . $this->version); + } + + return $this->checksum; + } + + protected function createFilename() + { + return "{$this->cacheFolder}/{$this->name()->name}.php"; + } + + /** + * Create configuration object. + * + * @param array $data + */ + abstract protected function createObject(array $data = []); + + /** + * Load single configuration file and append it to the correct position. + * + * @param string $name Name of the position. + * @param string $filename File to be loaded. + */ + abstract protected function loadFile($name, $filename); + + /** + * Load and join all configuration files. + * + * @return bool + * @internal + */ + protected function loadFiles() + { + $this->createObject(); + + $list = array_reverse($this->files); + foreach ($list as $files) { + foreach ($files as $name => $item) { + $this->loadFile($name, $this->path . $item['file']); + } + } + + return true; + } + + /** + * Load compiled file. + * + * @param string $filename + * @return bool + * @internal + */ + protected function loadCompiledFile($filename) + { + if (!file_exists($filename)) { + return false; + } + + $cache = include $filename; + if ( + !is_array($cache) + || !isset($cache['checksum']) + || !isset($cache['data']) + || !isset($cache['@class']) + || $cache['@class'] != get_class($this) + ) { + return false; + } + + // Load real file if cache isn't up to date (or is invalid). + if ($cache['checksum'] !== $this->checksum()) { + return false; + } + + $this->createObject($cache['data']); + + return true; + } + + /** + * Save compiled file. + * + * @param string $filename + * @throws \RuntimeException + * @internal + */ + protected function saveCompiledFile($filename) + { + $file = PhpFile::instance($filename); + + // Attempt to lock the file for writing. + try { + $file->lock(false); + } catch (\Exception $e) { + // Another process has locked the file; we will check this in a bit. + } + + if ($file->locked() === false) { + // File was already locked by another process. + return; + } + + $cache = [ + '@class' => get_class($this), + 'timestamp' => time(), + 'checksum' => $this->checksum(), + 'files' => $this->files, + 'data' => $this->object->toArray() + ]; + + $file->save($cache); + $file->unlock(); + $file->free(); + + $this->modified = true; + } +} diff --git a/system/src/Grav/Common/Config/CompiledBlueprints.php b/system/src/Grav/Common/Config/CompiledBlueprints.php new file mode 100644 index 000000000..b4b4594e5 --- /dev/null +++ b/system/src/Grav/Common/Config/CompiledBlueprints.php @@ -0,0 +1,44 @@ +object = new Blueprints($data); + } + + /** + * Load single configuration file and append it to the correct position. + * + * @param string $name Name of the position. + * @param string $filename File to be loaded. + */ + protected function loadFile($name, $filename) + { + $file = CompiledYamlFile::instance($filename); + $this->object->embed($name, $file->content(), '/'); + $file->free(); + } +} diff --git a/system/src/Grav/Common/Config/CompiledConfig.php b/system/src/Grav/Common/Config/CompiledConfig.php new file mode 100644 index 000000000..70593df33 --- /dev/null +++ b/system/src/Grav/Common/Config/CompiledConfig.php @@ -0,0 +1,88 @@ +callable = $blueprints; + + return $this; + } + + /** + * @param bool $withDefaults + * @return mixed + */ + public function load($withDefaults = false) + { + $this->withDefaults = $withDefaults; + + return parent::load(); + } + + /** + * Create configuration object. + * + * @param array $data + */ + protected function createObject(array $data = []) + { + if ($this->withDefaults && empty($data) && is_callable($this->callable)) { + $blueprints = $this->callable; + $data = $blueprints()->getDefaults(); + } + + $this->object = new Config($data, $this->callable); + $this->object->checksum($this->checksum()); + $this->object->modified($this->modified()); + + if (method_exists($this->object, 'prepare')) { + $this->object->prepare(); + } + } + + /** + * Load single configuration file and append it to the correct position. + * + * @param string $name Name of the position. + * @param string $filename File to be loaded. + */ + protected function loadFile($name, $filename) + { + $file = CompiledYamlFile::instance($filename); + $this->object->join($name, $file->content(), '/'); + $file->free(); + } +} diff --git a/system/src/Grav/Common/Config/CompiledLanguages.php b/system/src/Grav/Common/Config/CompiledLanguages.php new file mode 100644 index 000000000..29c1c81ef --- /dev/null +++ b/system/src/Grav/Common/Config/CompiledLanguages.php @@ -0,0 +1,48 @@ +object = new Languages($data); + $this->object->checksum($this->checksum()); + } + + /** + * Load single configuration file and append it to the correct position. + * + * @param string $name Name of the position. + * @param string $filename File to be loaded. + */ + protected function loadFile($name, $filename) + { + $file = CompiledYamlFile::instance($filename); + if (preg_match('|languages\.yaml$|', $filename)) { + $this->object->mergeRecursive($file->content()); + } else { + $this->object->join($name, $file->content(), '/'); + } + $file->free(); + } +} diff --git a/system/src/Grav/Common/Config/Config.php b/system/src/Grav/Common/Config/Config.php index ed13dbcbd..2fd3cc66d 100644 --- a/system/src/Grav/Common/Config/Config.php +++ b/system/src/Grav/Common/Config/Config.php @@ -1,12 +1,8 @@ [ - 'type' => 'ReadOnlyStream', - 'prefixes' => [ - '' => ['system'], - ] - ], - 'user' => [ - 'type' => 'ReadOnlyStream', - 'prefixes' => [ - '' => ['user'], - ] - ], - 'asset' => [ - 'type' => 'ReadOnlyStream', - 'prefixes' => [ - '' => ['assets'], - ] - ], - 'blueprints' => [ - 'type' => 'ReadOnlyStream', - 'prefixes' => [ - '' => ['user://blueprints', 'system/blueprints'], - ] - ], - 'config' => [ - 'type' => 'ReadOnlyStream', - 'prefixes' => [ - '' => ['user://config', 'system/config'], - ] - ], - 'plugins' => [ - 'type' => 'ReadOnlyStream', - 'prefixes' => [ - '' => ['user://plugins'], - ] - ], - 'plugin' => [ - 'type' => 'ReadOnlyStream', - 'prefixes' => [ - '' => ['user://plugins'], - ] - ], - 'themes' => [ - 'type' => 'ReadOnlyStream', - 'prefixes' => [ - '' => ['user://themes'], - ] - ], - 'languages' => [ - 'type' => 'ReadOnlyStream', - 'prefixes' => [ - '' => ['user://languages', 'system/languages'], - ] - ], - 'cache' => [ - 'type' => 'Stream', - 'prefixes' => [ - '' => ['cache'], - 'images' => ['images'] - ] - ], - 'log' => [ - 'type' => 'Stream', - 'prefixes' => [ - '' => ['logs'] - ] - ], - 'backup' => [ - 'type' => 'Stream', - 'prefixes' => [ - '' => ['backup'] - ] - ] - ]; - - protected $setup = []; - - protected $blueprintFiles = []; - protected $configFiles = []; - protected $languageFiles = []; protected $checksum; - protected $timestamp; - - protected $configLookup; - protected $blueprintLookup; - protected $pluginLookup; - protected $languagesLookup; - - protected $finder; - protected $environment; - protected $messages = []; - protected $languages; - - public function __construct(array $setup = array(), Grav $grav = null, $environment = null) + public function key() { - $this->grav = $grav ?: Grav::instance(); - $this->finder = new ConfigFinder; - $this->environment = $environment ?: 'localhost'; - $this->messages[] = 'Environment Name: ' . $this->environment; + return $this->checksum(); + } - // Make sure that - if (!isset($setup['streams']['schemes'])) { - $setup['streams']['schemes'] = []; + public function checksum($checksum = null) + { + if ($checksum !== null) { + $this->checksum = $checksum; } - $setup['streams']['schemes'] += $this->streams; - - $setup = $this->autoDetectEnvironmentConfig($setup); - - $this->setup = $setup; - parent::__construct($setup); - $this->check(); + return $this->checksum; } - public function key() + public function modified($modified = null) { - return $this->checksum(); + if ($modified !== null) { + $this->modified = $modified; + } + + return $this->modified; } public function reload() { + throw new \Exception('TODO'); $this->items = $this->setup; - $this->check(); $this->init(); $this->debug(); return $this; } - protected function check() - { - $streams = isset($this->items['streams']['schemes']) ? $this->items['streams']['schemes'] : null; - if (!is_array($streams)) { - throw new \InvalidArgumentException('Configuration is missing streams.schemes!'); - } - $diff = array_keys(array_diff_key($this->streams, $streams)); - if ($diff) { - throw new \InvalidArgumentException( - sprintf('Configuration is missing keys %s from streams.schemes!', implode(', ', $diff)) - ); - } - } - public function debug() { - foreach ($this->messages as $message) { - $this->grav['debugger']->addMessage($message); + $debugger = Grav::instance()['debugger']; + $debugger->addMessage('Environment Name: ' . $this->environment); + if ($this->modified()) { + $debugger->addMessage('Configuration reloaded and cached.'); } - $this->messages = []; } public function init() { - /** @var UniformResourceLocator $locator */ - $locator = $this->grav['locator']; - - $this->configLookup = $locator->findResources('config://'); - $this->blueprintLookup = $locator->findResources('blueprints://config'); - $this->pluginLookup = $locator->findResources('plugins://'); - - - $this->loadCompiledBlueprints($this->blueprintLookup, $this->pluginLookup, 'master'); - $this->loadCompiledConfig($this->configLookup, $this->pluginLookup, 'master'); - - // process languages if supported - if ($this->get('system.languages.translations', true)) { - $this->languagesLookup = $locator->findResources('languages://'); - $this->loadCompiledLanguages($this->languagesLookup, $this->pluginLookup, 'master'); - } - - $this->initializeLocator($locator); - } - - public function checksum() - { - if (empty($this->checksum)) { - $checkBlueprints = $this->get('system.cache.check.blueprints', false); - $checkLanguages = $this->get('system.cache.check.languages', false); - $checkConfig = $this->get('system.cache.check.config', true); - $checkSystem = $this->get('system.cache.check.system', true); - - if (!$checkBlueprints && !$checkLanguages && !$checkConfig && !$checkSystem) { - $this->messages[] = 'Skip configuration timestamp check.'; - return false; - } - - // Generate checksum according to the configuration settings. - if (!$checkConfig) { - // Just check changes in system.yaml files and ignore all the other files. - $cc = $checkSystem ? $this->finder->locateConfigFile($this->configLookup, 'system') : []; - } else { - // Check changes in all configuration files. - $cc = $this->finder->locateConfigFiles($this->configLookup, $this->pluginLookup); - } - - if ($checkBlueprints) { - $cb = $this->finder->locateBlueprintFiles($this->blueprintLookup, $this->pluginLookup); - } else { - $cb = []; - } - - if ($checkLanguages) { - $cl = $this->finder->locateLanguageFiles($this->languagesLookup, $this->pluginLookup); + $setup = Grav::instance()['setup']->toArray(); + foreach ($setup as $key => $value) { + if ($key === 'streams' || !is_array($value)) { + // Optimized as streams and simple values are fully defined in setup. + $this->items[$key] = $value; } else { - $cl = []; - } - - $this->checksum = md5(json_encode([$cc, $cb, $cl])); - } - - return $this->checksum; - } - - protected function autoDetectEnvironmentConfig($items) - { - $environment = $this->environment; - $env_stream = 'user://'.$environment.'/config'; - - if (file_exists(USER_DIR.$environment.'/config')) { - array_unshift($items['streams']['schemes']['config']['prefixes'][''], $env_stream); - } - - return $items; - } - - protected function loadCompiledBlueprints($blueprints, $plugins, $filename = null) - { - $checksum = md5(json_encode($blueprints)); - $filename = $filename - ? CACHE_DIR . 'compiled/blueprints/' . $filename . '-' . $this->environment . '.php' - : CACHE_DIR . 'compiled/blueprints/' . $checksum . '-' . $this->environment . '.php'; - $file = PhpFile::instance($filename); - $cache = $file->exists() ? $file->content() : null; - $blueprintFiles = $this->finder->locateBlueprintFiles($blueprints, $plugins); - $checksum .= ':'.md5(json_encode($blueprintFiles)); - $class = get_class($this); - - // Load real file if cache isn't up to date (or is invalid). - if ( - !is_array($cache) - || !isset($cache['checksum']) - || !isset($cache['@class']) - || $cache['checksum'] != $checksum - || $cache['@class'] != $class - ) { - // Attempt to lock the file for writing. - $file->lock(false); - - // Load blueprints. - $this->blueprints = new Blueprints; - foreach ($blueprintFiles as $files) { - $this->loadBlueprintFiles($files); - } - - $cache = [ - '@class' => $class, - 'checksum' => $checksum, - 'files' => $blueprintFiles, - 'data' => $this->blueprints->toArray() - ]; - // If compiled file wasn't already locked by another process, save it. - if ($file->locked() !== false) { - $this->messages[] = 'Saving compiled blueprints.'; - $file->save($cache); - $file->unlock(); - } - } else { - $this->blueprints = new Blueprints($cache['data']); - } - } - - protected function loadCompiledConfig($configs, $plugins, $filename = null) - { - $checksum = md5(json_encode($configs)); - $filename = $filename - ? CACHE_DIR . 'compiled/config/' . $filename . '-' . $this->environment . '.php' - : CACHE_DIR . 'compiled/config/' . $checksum . '-' . $this->environment . '.php'; - $file = PhpFile::instance($filename); - $cache = $file->exists() ? $file->content() : null; - $class = get_class($this); - $checksum = $this->checksum(); - - if ( - !is_array($cache) - || !isset($cache['checksum']) - || !isset($cache['@class']) - || $cache['@class'] != $class - ) { - $this->messages[] = 'No cached configuration, compiling new configuration..'; - } else if ($cache['checksum'] !== $checksum) { - $this->messages[] = 'Configuration checksum mismatch, reloading configuration..'; - } else { - $this->messages[] = 'Configuration checksum matches, using cached version.'; - - $this->items = $cache['data']; - return; - } - - $configFiles = $this->finder->locateConfigFiles($configs, $plugins); - - // Attempt to lock the file for writing. - $file->lock(false); - - // Load configuration. - foreach ($configFiles as $files) { - $this->loadConfigFiles($files); - } - $cache = [ - '@class' => $class, - 'timestamp' => time(), - 'checksum' => $checksum, - 'data' => $this->toArray() - ]; - - // If compiled file wasn't already locked by another process, save it. - if ($file->locked() !== false) { - $this->messages[] = 'Saving compiled configuration.'; - $file->save($cache); - $file->unlock(); - } - - $this->items = $cache['data']; - } - - /** - * @param $languages - * @param $plugins - * @param null $filename - */ - protected function loadCompiledLanguages($languages, $plugins, $filename = null) - { - $checksum = md5(json_encode($languages)); - $filename = $filename - ? CACHE_DIR . 'compiled/languages/' . $filename . '-' . $this->environment . '.php' - : CACHE_DIR . 'compiled/languages/' . $checksum . '-' . $this->environment . '.php'; - $file = PhpFile::instance($filename); - $cache = $file->exists() ? $file->content() : null; - $languageFiles = $this->finder->locateLanguageFiles($languages, $plugins); - $checksum .= ':' . md5(json_encode($languageFiles)); - $class = get_class($this); - - // Load real file if cache isn't up to date (or is invalid). - if ( - !is_array($cache) - || !isset($cache['checksum']) - || !isset($cache['@class']) - || $cache['checksum'] != $checksum - || $cache['@class'] != $class - ) { - // Attempt to lock the file for writing. - $file->lock(false); - - // Load languages. - $this->languages = new Languages; - $pluginPaths = str_ireplace(GRAV_ROOT . '/', '', array_reverse($plugins)); - foreach ($pluginPaths as $path) { - if (isset($languageFiles[$path])) { - foreach ((array) $languageFiles[$path] as $plugin => $item) { - $lang_file = CompiledYamlFile::instance($item['file']); - $content = $lang_file->content(); - $this->languages->mergeRecursive($content); - } - unset($languageFiles[$path]); - } - } - - foreach ($languageFiles as $location) { - foreach ($location as $lang => $item) { - $lang_file = CompiledYamlFile::instance($item['file']); - $content = $lang_file->content(); - $this->languages->join($lang, $content, '/'); - } - } - - $cache = [ - '@class' => $class, - 'checksum' => $checksum, - 'files' => $languageFiles, - 'data' => $this->languages->toArray() - ]; - // If compiled file wasn't already locked by another process, save it. - if ($file->locked() !== false) { - $this->messages[] = 'Saving compiled languages.'; - $file->save($cache); - $file->unlock(); - } - } else { - $this->languages = new Languages($cache['data']); - } - } - - /** - * Load blueprints. - * - * @param array $files - */ - public function loadBlueprintFiles(array $files) - { - foreach ($files as $name => $item) { - $file = CompiledYamlFile::instance($item['file']); - $this->blueprints->embed($name, $file->content(), '/'); - } - } - - /** - * Load configuration. - * - * @param array $files - */ - public function loadConfigFiles(array $files) - { - foreach ($files as $name => $item) { - $file = CompiledYamlFile::instance($item['file']); - $this->join($name, $file->content(), '/'); - } - } - - /** - * Initialize resource locator by using the configuration. - * - * @param UniformResourceLocator $locator - */ - public function initializeLocator(UniformResourceLocator $locator) - { - $locator->reset(); - - $schemes = (array) $this->get('streams.schemes', []); - - foreach ($schemes as $scheme => $config) { - if (isset($config['paths'])) { - $locator->addPath($scheme, '', $config['paths']); - } - if (isset($config['prefixes'])) { - foreach ($config['prefixes'] as $prefix => $paths) { - $locator->addPath($scheme, $prefix, $paths); - } + $this->joinDefaults($key, $value); } } } /** - * Get available streams and their types from the configuration. - * - * @return array + * @return mixed + * @deprecated */ - public function getStreams() - { - $schemes = []; - foreach ((array) $this->get('streams.schemes') as $scheme => $config) { - $type = !empty($config['type']) ? $config['type'] : 'ReadOnlyStream'; - if ($type[0] != '\\') { - $type = '\\RocketTheme\\Toolbox\\StreamWrapper\\' . $type; - } - - $schemes[$scheme] = $type; - } - - return $schemes; - } - public function getLanguages() { - return $this->languages; + return Grav::instance()['languages']; } } diff --git a/system/src/Grav/Common/Config/ConfigFileFinder.php b/system/src/Grav/Common/Config/ConfigFileFinder.php new file mode 100644 index 000000000..887575a95 --- /dev/null +++ b/system/src/Grav/Common/Config/ConfigFileFinder.php @@ -0,0 +1,258 @@ +base = $base ? "{$base}/" : ''; + + return $this; + } + + /** + * Return all locations for all the files with a timestamp. + * + * @param array $paths List of folders to look from. + * @param string $pattern Pattern to match the file. Pattern will also be removed from the key. + * @param int $levels Maximum number of recursive directories. + * @return array + */ + public function locateFiles(array $paths, $pattern = '|\.yaml$|', $levels = -1) + { + $list = []; + foreach ($paths as $folder) { + $list += $this->detectRecursive($folder, $pattern, $levels); + } + return $list; + } + + /** + * Return all locations for all the files with a timestamp. + * + * @param array $paths List of folders to look from. + * @param string $pattern Pattern to match the file. Pattern will also be removed from the key. + * @param int $levels Maximum number of recursive directories. + * @return array + */ + public function getFiles(array $paths, $pattern = '|\.yaml$|', $levels = -1) + { + $list = []; + foreach ($paths as $folder) { + $path = trim(Folder::getRelativePath($folder), '/'); + + $files = $this->detectRecursive($folder, $pattern, $levels); + + $list += $files[trim($path, '/')]; + } + return $list; + } + + /** + * Return all paths for all the files with a timestamp. + * + * @param array $paths List of folders to look from. + * @param string $pattern Pattern to match the file. Pattern will also be removed from the key. + * @param int $levels Maximum number of recursive directories. + * @return array + */ + public function listFiles(array $paths, $pattern = '|\.yaml$|', $levels = -1) + { + $list = []; + foreach ($paths as $folder) { + $list = array_merge_recursive($list, $this->detectAll($folder, $pattern, $levels)); + } + return $list; + } + + /** + * Find filename from a list of folders. + * + * Note: Only finds the last override. + * + * @param string $filename + * @param array $folders + * @return array + */ + public function locateFileInFolder($filename, array $folders) + { + $list = []; + foreach ($folders as $folder) { + $list += $this->detectInFolder($folder, $filename); + } + return $list; + } + + /** + * Find filename from a list of folders. + * + * @param array $folders + * @param string $filename + * @return array + */ + public function locateInFolders(array $folders, $filename = null) + { + $list = []; + foreach ($folders as $folder) { + $path = trim(Folder::getRelativePath($folder), '/'); + $list[$path] = $this->detectInFolder($folder, $filename); + } + return $list; + } + + /** + * Return all existing locations for a single file with a timestamp. + * + * @param array $paths Filesystem paths to look up from. + * @param string $name Configuration file to be located. + * @param string $ext File extension (optional, defaults to .yaml). + * @return array + */ + public function locateFile(array $paths, $name, $ext = '.yaml') + { + $filename = preg_replace('|[.\/]+|', '/', $name) . $ext; + + $list = []; + foreach ($paths as $folder) { + $path = trim(Folder::getRelativePath($folder), '/'); + + if (is_file("{$folder}/{$filename}")) { + $modified = filemtime("{$folder}/{$filename}"); + } else { + $modified = 0; + } + $basename = $this->base . $name; + $list[$path] = [$basename => ['file' => "{$path}/{$filename}", 'modified' => $modified]]; + } + + return $list; + } + + /** + * Detects all directories with a configuration file and returns them with last modification time. + * + * @param string $folder Location to look up from. + * @param string $pattern Pattern to match the file. Pattern will also be removed from the key. + * @param int $levels Maximum number of recursive directories. + * @return array + * @internal + */ + protected function detectRecursive($folder, $pattern, $levels) + { + $path = trim(Folder::getRelativePath($folder), '/'); + + if (is_dir($folder)) { + // Find all system and user configuration files. + $options = [ + 'levels' => $levels, + 'compare' => 'Filename', + 'pattern' => $pattern, + 'filters' => [ + 'pre-key' => $this->base, + 'key' => $pattern, + 'value' => function (\RecursiveDirectoryIterator $file) use ($path) { + return ['file' => "{$path}/{$file->getSubPathname()}", 'modified' => $file->getMTime()]; + } + ], + 'key' => 'SubPathname' + ]; + + $list = Folder::all($folder, $options); + + ksort($list); + } else { + $list = []; + } + + return [$path => $list]; + } + + /** + * Detects all directories with the lookup file and returns them with last modification time. + * + * @param string $folder Location to look up from. + * @param string $lookup Filename to be located (defaults to directory name). + * @return array + * @internal + */ + protected function detectInFolder($folder, $lookup = null) + { + $folder = rtrim($folder, '/'); + $path = trim(Folder::getRelativePath($folder), '/'); + $base = $path === $folder ? '' : ($path ? substr($folder, 0, -strlen($path)) : $folder . '/'); + + $list = []; + + if (is_dir($folder)) { + $iterator = new \DirectoryIterator($folder); + + /** @var \DirectoryIterator $directory */ + foreach ($iterator as $directory) { + if (!$directory->isDir() || $directory->isDot()) { + continue; + } + + $name = $directory->getBasename(); + $find = ($lookup ?: $name) . '.yaml'; + $filename = "{$path}/{$name}/{$find}"; + + if (file_exists($base . $filename)) { + $basename = $this->base . $name; + $list[$basename] = ['file' => $filename, 'modified' => filemtime($base . $filename)]; + } + } + } + + return $list; + } + + /** + * Detects all plugins with a configuration file and returns them with last modification time. + * + * @param string $folder Location to look up from. + * @param string $pattern Pattern to match the file. Pattern will also be removed from the key. + * @param int $levels Maximum number of recursive directories. + * @return array + * @internal + */ + protected function detectAll($folder, $pattern, $levels) + { + $path = trim(Folder::getRelativePath($folder), '/'); + + if (is_dir($folder)) { + // Find all system and user configuration files. + $options = [ + 'levels' => $levels, + 'compare' => 'Filename', + 'pattern' => $pattern, + 'filters' => [ + 'pre-key' => $this->base, + 'key' => $pattern, + 'value' => function (\RecursiveDirectoryIterator $file) use ($path) { + return ["{$path}/{$file->getSubPathname()}" => $file->getMTime()]; + } + ], + 'key' => 'SubPathname' + ]; + + $list = Folder::all($folder, $options); + + ksort($list); + } else { + $list = []; + } + + return $list; + } +} diff --git a/system/src/Grav/Common/Config/ConfigFinder.php b/system/src/Grav/Common/Config/ConfigFinder.php deleted file mode 100644 index 46069410d..000000000 --- a/system/src/Grav/Common/Config/ConfigFinder.php +++ /dev/null @@ -1,186 +0,0 @@ -detectInFolder($folder, 'blueprints'); - } - foreach (array_reverse($blueprints) as $folder) { - $list += $this->detectRecursive($folder); - } - return $list; - } - - /** - * Get all locations for configuration files (including plugins). - * - * @param array $configs - * @param array $plugins - * @return array - */ - public function locateConfigFiles(array $configs, array $plugins) - { - $list = []; - foreach (array_reverse($plugins) as $folder) { - $list += $this->detectInFolder($folder); - } - foreach (array_reverse($configs) as $folder) { - $list += $this->detectRecursive($folder); - } - return $list; - } - - public function locateLanguageFiles(array $languages, array $plugins) - { - $list = []; - foreach (array_reverse($plugins) as $folder) { - $list += $this->detectLanguagesInFolder($folder, 'languages'); - } - foreach (array_reverse($languages) as $folder) { - $list += $this->detectRecursive($folder); - } - return $list; - } - - /** - * Get all locations for a single configuration file. - * - * @param array $folders Locations to look up from. - * @param string $name Filename to be located. - * @return array - */ - public function locateConfigFile(array $folders, $name) - { - $filename = "{$name}.yaml"; - - $list = []; - foreach ($folders as $folder) { - $path = trim(Folder::getRelativePath($folder), '/'); - - if (is_file("{$folder}/{$filename}")) { - $modified = filemtime("{$folder}/{$filename}"); - } else { - $modified = 0; - } - $list[$path] = [$name => ['file' => "{$path}/{$filename}", 'modified' => $modified]]; - } - - return $list; - } - - /** - * Detects all plugins with a configuration file and returns them with last modification time. - * - * @param string $folder Location to look up from. - * @param string $lookup Filename to be located. - * @return array - * @internal - */ - protected function detectInFolder($folder, $lookup = null) - { - $path = trim(Folder::getRelativePath($folder), '/'); - - $list = []; - - if (is_dir($folder)) { - $iterator = new \FilesystemIterator($folder); - - /** @var \DirectoryIterator $directory */ - foreach ($iterator as $directory) { - if (!$directory->isDir()) { - continue; - } - - $name = $directory->getBasename(); - $find = ($lookup ?: $name) . '.yaml'; - $filename = "{$path}/{$name}/$find"; - - if (file_exists($filename)) { - $list["plugins/{$name}"] = ['file' => $filename, 'modified' => filemtime($filename)]; - } - } - } - - return [$path => $list]; - } - - protected function detectLanguagesInFolder($folder, $lookup = null) - { - $path = trim(Folder::getRelativePath($folder), '/'); - - $list = []; - - if (is_dir($folder)) { - $iterator = new \FilesystemIterator($folder); - - /** @var \DirectoryIterator $directory */ - foreach ($iterator as $directory) { - if (!$directory->isDir()) { - continue; - } - - $name = $directory->getBasename(); - $find = ($lookup ?: $name) . '.yaml'; - $filename = "{$path}/{$name}/$find"; - - if (file_exists($filename)) { - $list[$name] = ['file' => $filename, 'modified' => filemtime($filename)]; - } - } - } - - return [$path => $list]; - } - - /** - * Detects all plugins with a configuration file and returns them with last modification time. - * - * @param string $folder Location to look up from. - * @return array - * @internal - */ - protected function detectRecursive($folder) - { - $path = trim(Folder::getRelativePath($folder), '/'); - - if (is_dir($folder)) { - // Find all system and user configuration files. - $options = [ - 'compare' => 'Filename', - 'pattern' => '|\.yaml$|', - 'filters' => [ - 'key' => '|\.yaml$|', - 'value' => function (\RecursiveDirectoryIterator $file) use ($path) { - return ['file' => "{$path}/{$file->getSubPathname()}", 'modified' => $file->getMTime()]; - } - ], - 'key' => 'SubPathname' - ]; - - $list = Folder::all($folder, $options); - } else { - $list = []; - } - - return [$path => $list]; - } -} diff --git a/system/src/Grav/Common/Config/Languages.php b/system/src/Grav/Common/Config/Languages.php index c0141292e..1ea8b3c87 100644 --- a/system/src/Grav/Common/Config/Languages.php +++ b/system/src/Grav/Common/Config/Languages.php @@ -12,6 +12,15 @@ class Languages extends Data { + public function checksum($checksum = null) + { + if ($checksum !== null) { + $this->checksum = $checksum; + } + + return $this->checksum; + } + public function reformat() { if (isset($this->items['plugins'])) { diff --git a/system/src/Grav/Common/Config/Setup.php b/system/src/Grav/Common/Config/Setup.php new file mode 100644 index 000000000..ef89df015 --- /dev/null +++ b/system/src/Grav/Common/Config/Setup.php @@ -0,0 +1,232 @@ + [ + 'type' => 'ReadOnlyStream', + 'prefixes' => [ + '' => ['system'], + ] + ], + 'user' => [ + 'type' => 'ReadOnlyStream', + 'prefixes' => [ + '' => ['user'], + ] + ], + 'environment' => [ + 'type' => 'ReadOnlyStream' + // If not defined, environment will be set up in the constructor. + ], + 'asset' => [ + 'type' => 'ReadOnlyStream', + 'prefixes' => [ + '' => ['assets'], + ] + ], + 'blueprints' => [ + 'type' => 'ReadOnlyStream', + 'prefixes' => [ + '' => ['environment://blueprints', 'user://blueprints', 'system/blueprints'], + ] + ], + 'config' => [ + 'type' => 'ReadOnlyStream', + 'prefixes' => [ + '' => ['environment://config', 'user://config', 'system/config'], + ] + ], + 'plugins' => [ + 'type' => 'ReadOnlyStream', + 'prefixes' => [ + '' => ['user://plugins'], + ] + ], + 'plugin' => [ + 'type' => 'ReadOnlyStream', + 'prefixes' => [ + '' => ['user://plugins'], + ] + ], + 'themes' => [ + 'type' => 'ReadOnlyStream', + 'prefixes' => [ + '' => ['user://themes'], + ] + ], + 'languages' => [ + 'type' => 'ReadOnlyStream', + 'prefixes' => [ + '' => ['environment://languages', 'user://languages', 'system/languages'], + ] + ], + 'cache' => [ + 'type' => 'Stream', + 'prefixes' => [ + '' => ['cache'], + 'images' => ['images'] + ] + ], + 'log' => [ + 'type' => 'Stream', + 'prefixes' => [ + '' => ['logs'] + ] + ], + 'backup' => [ + 'type' => 'Stream', + 'prefixes' => [ + '' => ['backup'] + ] + ], + 'image' => [ + 'type' => 'ReadOnlyStream', + 'prefixes' => [ + '' => ['user://images', 'system://images'] + ] + ], + 'page' => [ + 'type' => 'ReadOnlyStream', + 'prefixes' => [ + '' => ['user://pages'] + ] + ], + 'account' => [ + 'type' => 'ReadOnlyStream', + 'prefixes' => [ + '' => ['user://accounts'] + ] + ], + ]; + + public function __construct($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'; + $setup = is_file($file) ? (array) include $file : []; + + // Add default streams defined in beginning of the class. + if (!isset($setup['streams']['schemes'])) { + $setup['streams']['schemes'] = []; + } + $setup['streams']['schemes'] += $this->streams; + + // Initialize class. + parent::__construct($setup); + + // Set up environment. + $this->def('environment', $environment); + $this->def('streams.schemes.environment.prefixes', ['' => ["user://{$this->environment}"]]); + } + + /** + * @return $this + */ + public function init() + { + $locator = new UniformResourceLocator(GRAV_ROOT); + $files = []; + + $guard = 5; + do { + $check = $files; + $this->initializeLocator($locator); + $files = $locator->findResources('config://streams.yaml'); + + if ($check === $files) { + break; + } + + // Update streams. + foreach ($files as $path) { + $file = CompiledYamlFile::instance($path); + $content = $file->content(); + if (!empty($content['schemes'])) { + $this->items['streams']['schemes'] = $content['schemes'] + $this->items['streams']['schemes']; + } + } + } while (--$guard); + + if (!$guard) { + throw new \RuntimeException('Setup: Configuration reload loop detected!'); + } + + // Make sure we have valid setup. + $this->check(); + + return $this; + } + + /** + * Initialize resource locator by using the configuration. + * + * @param UniformResourceLocator $locator + */ + public function initializeLocator(UniformResourceLocator $locator) + { + $locator->reset(); + + $schemes = (array) $this->get('streams.schemes', []); + + foreach ($schemes as $scheme => $config) { + if (isset($config['paths'])) { + $locator->addPath($scheme, '', $config['paths']); + } + if (isset($config['prefixes'])) { + foreach ($config['prefixes'] as $prefix => $paths) { + $locator->addPath($scheme, $prefix, $paths); + } + } + } + } + + /** + * Get available streams and their types from the configuration. + * + * @return array + */ + public function getStreams() + { + $schemes = []; + foreach ((array) $this->get('streams.schemes') as $scheme => $config) { + $type = !empty($config['type']) ? $config['type'] : 'ReadOnlyStream'; + if ($type[0] != '\\') { + $type = '\\RocketTheme\\Toolbox\\StreamWrapper\\' . $type; + } + + $schemes[$scheme] = $type; + } + + return $schemes; + } + + /** + * @throws \InvalidArgumentException + */ + protected function check() + { + $streams = isset($this->items['streams']['schemes']) ? $this->items['streams']['schemes'] : null; + if (!is_array($streams)) { + throw new \InvalidArgumentException('Configuration is missing streams.schemes!'); + } + $diff = array_keys(array_diff_key($this->streams, $streams)); + if ($diff) { + throw new \InvalidArgumentException( + sprintf('Configuration is missing keys %s from streams.schemes!', implode(', ', $diff)) + ); + } + } +} diff --git a/system/src/Grav/Common/Service/ConfigServiceProvider.php b/system/src/Grav/Common/Service/ConfigServiceProvider.php index 28a6ac4d9..febdcd6a7 100644 --- a/system/src/Grav/Common/Service/ConfigServiceProvider.php +++ b/system/src/Grav/Common/Service/ConfigServiceProvider.php @@ -1,10 +1,15 @@ init(); + }; - // Pre-load setup.php as it contains our initial configuration. - $file = GRAV_ROOT . '/setup.php'; - $this->setup = is_file($file) ? (array) include $file : []; - $this->environment = isset($this->setup['environment']) ? $this->setup['environment'] : null; + $container['blueprints'] = function ($c) { + return static::blueprints($c); + }; - $container['blueprints'] = function ($c) use ($self) { - return $self->loadMasterBlueprints($c); + $container['config'] = function ($c) { + return static::load($c); }; - $container['config'] = function ($c) use ($self) { - return $self->loadMasterConfig($c); + $container['languages'] = function ($c) { + return static::languages($c); }; } - public function loadMasterConfig(Container $container) + public static function setup(Container $container) { - $environment = $this->getEnvironment($container); + return new Setup($container['uri']->environment()); + } + + public static function blueprints(Container $container) + { + /** Setup $setup */ + $setup = $container['setup']; + + /** @var UniformResourceLocator $locator */ + $locator = $container['locator']; + + $cache = $locator->findResource('cache://compiled/blueprints', true, true); + + $files = []; + $paths = $locator->findResources('blueprints://config'); + $files += (new ConfigFileFinder)->locateFiles($paths); + $paths = $locator->findResources('plugins://'); + $files += (new ConfigFileFinder)->setBase('plugins')->locateInFolders($paths, 'blueprints'); - $config = new Config($this->setup, $container, $environment); + $blueprints = new CompiledBlueprints($cache, $files, GRAV_ROOT); - return $config; + return $blueprints->name("master-{$setup->environment}")->load(); } - public function loadMasterBlueprints(Container $container) + public static function load(Container $container) { - $environment = $this->getEnvironment($container); - $file = CACHE_DIR . 'compiled/blueprints/master-'.$environment.'.php'; - $data = is_file($file) ? (array) include $file : []; + /** Setup $setup */ + $setup = $container['setup']; - return new Blueprints($data, $container); + /** @var UniformResourceLocator $locator */ + $locator = $container['locator']; + + $cache = $locator->findResource('cache://compiled/config', true, true); + + $files = []; + $paths = $locator->findResources('config://'); + $files += (new ConfigFileFinder)->locateFiles($paths); + $paths = $locator->findResources('plugins://'); + $files += (new ConfigFileFinder)->setBase('plugins')->locateInFolders($paths); + + $config = new CompiledConfig($cache, $files, GRAV_ROOT); + $config->setBlueprints(function() use ($container) { + return $container['blueprints']; + }); + + return $config->name("master-{$setup->environment}")->load(); } - public function getEnvironment(Container $container) + public static function languages(Container $container) { - if (!isset($this->environment)) { - $this->environment = $container['uri']->environment(); + /** Setup $setup */ + $setup = $container['setup']; + + /** @var Config $config */ + $config = $container['config']; + + /** @var UniformResourceLocator $locator */ + $locator = $container['locator']; + + $cache = $locator->findResource('cache://compiled/languages', true, true); + $files = []; + + // Process languages only if enabled in configuration. + if ($config->get('system.languages.translations', true)) { + $paths = $locator->findResources('languages://'); + $files += (new ConfigFileFinder)->locateFiles($paths); + $paths = $locator->findResources('plugins://'); + $files += (new ConfigFileFinder)->setBase('plugins')->locateInFolders($paths, 'languages'); } - return $this->environment; + $languages = new CompiledLanguages($cache, $files, GRAV_ROOT); + + return $languages->name("master-{$setup->environment}")->load(); } } diff --git a/system/src/Grav/Common/Service/StreamsServiceProvider.php b/system/src/Grav/Common/Service/StreamsServiceProvider.php index d8f7dffbb..ec7f9cd85 100644 --- a/system/src/Grav/Common/Service/StreamsServiceProvider.php +++ b/system/src/Grav/Common/Service/StreamsServiceProvider.php @@ -1,7 +1,7 @@ initializeLocator($locator); + /** @var Setup $setup */ + $setup = $c['setup']; + $setup->initializeLocator($locator); return $locator; }; $container['streams'] = function($c) { - /** @var Config $config */ - $config = $c['config']; + /** @var Setup $setup */ + $setup = $c['setup']; /** @var UniformResourceLocator $locator */ $locator = $c['locator']; @@ -34,7 +34,7 @@ public function register(Container $container) Stream::setLocator($locator); ReadOnlyStream::setLocator($locator); - return new StreamBuilder($config->getStreams($c)); + return new StreamBuilder($setup->getStreams($c)); }; } } From 7228b25393c1bcff22651669ab41837d39b08f61 Mon Sep 17 00:00:00 2001 From: Matias Griese Date: Thu, 19 Nov 2015 10:49:55 +0200 Subject: [PATCH 26/79] Fix configuration reload --- system/src/Grav/Common/Config/Config.php | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/system/src/Grav/Common/Config/Config.php b/system/src/Grav/Common/Config/Config.php index 2fd3cc66d..5bb1ff7c4 100644 --- a/system/src/Grav/Common/Config/Config.php +++ b/system/src/Grav/Common/Config/Config.php @@ -3,6 +3,7 @@ use Grav\Common\Grav; use Grav\Common\Data\Data; +use Grav\Common\Service\ConfigServiceProvider; /** * The Config class contains configuration information. @@ -39,10 +40,17 @@ public function modified($modified = null) public function reload() { - throw new \Exception('TODO'); - $this->items = $this->setup; - $this->init(); - $this->debug(); + $grav = Grav::instance(); + + // Load new configuration. + $config = ConfigServiceProvider::load($grav); + + // Update current configuration if needed. + if ($config->modified()) { + $this->items = $config->toArray(); + $this->checksum($config->checksum()); + $this->modified($config->modified()); + } return $this; } From 5dd1554e5d3117621292aba4ef9363e689101ddf Mon Sep 17 00:00:00 2001 From: Matias Griese Date: Thu, 19 Nov 2015 12:02:08 +0200 Subject: [PATCH 27/79] Fix modified function in config --- .../src/Grav/Common/Config/CompiledBase.php | 21 +++++++++---------- .../Grav/Common/Config/CompiledBlueprints.php | 5 +++++ .../src/Grav/Common/Config/CompiledConfig.php | 18 ++++++++++++---- .../Grav/Common/Config/CompiledLanguages.php | 16 ++++++++++++++ system/src/Grav/Common/Config/Config.php | 13 ++++++++++-- system/src/Grav/Common/Config/Languages.php | 10 ++++++++- 6 files changed, 65 insertions(+), 18 deletions(-) diff --git a/system/src/Grav/Common/Config/CompiledBase.php b/system/src/Grav/Common/Config/CompiledBase.php index d20223ef1..f861cef34 100644 --- a/system/src/Grav/Common/Config/CompiledBase.php +++ b/system/src/Grav/Common/Config/CompiledBase.php @@ -43,11 +43,6 @@ abstract class CompiledBase */ protected $object; - /** - * @var bool - */ - protected $modified = false; - /** * @param string $cacheFolder Cache folder to be used. * @param array $files List of files as returned from ConfigFileFinder class. @@ -81,12 +76,9 @@ public function name($name = null) } /** - * @return bool + * Function gets called when cached configuration is saved. */ - public function modified() - { - return $this->modified; - } + public function modified() {} /** * Load the configuration. @@ -135,6 +127,11 @@ protected function createFilename() */ abstract protected function createObject(array $data = []); + /** + * Finalize configuration object. + */ + abstract protected function finalizeObject(); + /** * Load single configuration file and append it to the correct position. * @@ -160,6 +157,8 @@ protected function loadFiles() } } + $this->finalizeObject(); + return true; } @@ -232,6 +231,6 @@ protected function saveCompiledFile($filename) $file->unlock(); $file->free(); - $this->modified = true; + $this->modified(); } } diff --git a/system/src/Grav/Common/Config/CompiledBlueprints.php b/system/src/Grav/Common/Config/CompiledBlueprints.php index b4b4594e5..d78a0e04e 100644 --- a/system/src/Grav/Common/Config/CompiledBlueprints.php +++ b/system/src/Grav/Common/Config/CompiledBlueprints.php @@ -29,6 +29,11 @@ protected function createObject(array $data = []) $this->object = new Blueprints($data); } + /** + * Finalize configuration object. + */ + protected function finalizeObject() {} + /** * Load single configuration file and append it to the correct position. * diff --git a/system/src/Grav/Common/Config/CompiledConfig.php b/system/src/Grav/Common/Config/CompiledConfig.php index 70593df33..cb9e59de2 100644 --- a/system/src/Grav/Common/Config/CompiledConfig.php +++ b/system/src/Grav/Common/Config/CompiledConfig.php @@ -65,12 +65,22 @@ protected function createObject(array $data = []) } $this->object = new Config($data, $this->callable); + } + + /** + * Finalize configuration object. + */ + protected function finalizeObject() + { $this->object->checksum($this->checksum()); - $this->object->modified($this->modified()); + } - if (method_exists($this->object, 'prepare')) { - $this->object->prepare(); - } + /** + * Function gets called when cached configuration is saved. + */ + public function modified() + { + $this->object->modified(true); } /** diff --git a/system/src/Grav/Common/Config/CompiledLanguages.php b/system/src/Grav/Common/Config/CompiledLanguages.php index 29c1c81ef..ce4eb59dc 100644 --- a/system/src/Grav/Common/Config/CompiledLanguages.php +++ b/system/src/Grav/Common/Config/CompiledLanguages.php @@ -26,9 +26,25 @@ class CompiledLanguages extends CompiledBase protected function createObject(array $data = []) { $this->object = new Languages($data); + } + + /** + * Finalize configuration object. + */ + protected function finalizeObject() + { $this->object->checksum($this->checksum()); } + + /** + * Function gets called when cached configuration is saved. + */ + public function modified() + { + $this->object->modified(true); + } + /** * Load single configuration file and append it to the correct position. * diff --git a/system/src/Grav/Common/Config/Config.php b/system/src/Grav/Common/Config/Config.php index 5bb1ff7c4..dc5169750 100644 --- a/system/src/Grav/Common/Config/Config.php +++ b/system/src/Grav/Common/Config/Config.php @@ -1,6 +1,7 @@ modified()) { + // Update current configuration. $this->items = $config->toArray(); $this->checksum($config->checksum()); - $this->modified($config->modified()); + $this->modified(true); + + $debugger->addMessage('Configuration was changed and saved.'); } return $this; @@ -57,7 +64,9 @@ public function reload() public function debug() { + /** @var Debugger $debugger */ $debugger = Grav::instance()['debugger']; + $debugger->addMessage('Environment Name: ' . $this->environment); if ($this->modified()) { $debugger->addMessage('Configuration reloaded and cached.'); diff --git a/system/src/Grav/Common/Config/Languages.php b/system/src/Grav/Common/Config/Languages.php index 1ea8b3c87..4d92d87e9 100644 --- a/system/src/Grav/Common/Config/Languages.php +++ b/system/src/Grav/Common/Config/Languages.php @@ -11,7 +11,6 @@ */ class Languages extends Data { - public function checksum($checksum = null) { if ($checksum !== null) { @@ -21,6 +20,15 @@ public function checksum($checksum = null) return $this->checksum; } + public function modified($modified = null) + { + if ($modified !== null) { + $this->modified = $modified; + } + + return $this->modified; + } + public function reformat() { if (isset($this->items['plugins'])) { From 09ed480628dd17ec8db168d3a013c05b8faf66bf Mon Sep 17 00:00:00 2001 From: Matias Griese Date: Thu, 19 Nov 2015 17:05:21 +0200 Subject: [PATCH 28/79] Composer update --- composer.lock | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/composer.lock b/composer.lock index 9328557aa..9b04239b3 100644 --- a/composer.lock +++ b/composer.lock @@ -217,12 +217,12 @@ "source": { "type": "git", "url": "https://github.com/filp/whoops.git", - "reference": "9a393ceb80f7497b6513feb574638e87048fed55" + "reference": "50a288b51058fa94cf5b37cfa4277535983cc9d5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/filp/whoops/zipball/9a393ceb80f7497b6513feb574638e87048fed55", - "reference": "9a393ceb80f7497b6513feb574638e87048fed55", + "url": "https://api.github.com/repos/filp/whoops/zipball/50a288b51058fa94cf5b37cfa4277535983cc9d5", + "reference": "50a288b51058fa94cf5b37cfa4277535983cc9d5", "shasum": "" }, "require": { @@ -267,7 +267,7 @@ "whoops", "zf2" ], - "time": "2015-09-27 09:47:06" + "time": "2015-11-14 20:08:27" }, { "name": "gregwar/cache", @@ -670,12 +670,12 @@ "source": { "type": "git", "url": "https://github.com/rockettheme/toolbox.git", - "reference": "0a7006b00880bd6873c49e2ed23939c785310417" + "reference": "defdf78a4c2f537067c6548c99560b78ed92745d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/rockettheme/toolbox/zipball/0a7006b00880bd6873c49e2ed23939c785310417", - "reference": "0a7006b00880bd6873c49e2ed23939c785310417", + "url": "https://api.github.com/repos/rockettheme/toolbox/zipball/defdf78a4c2f537067c6548c99560b78ed92745d", + "reference": "defdf78a4c2f537067c6548c99560b78ed92745d", "shasum": "" }, "require": { @@ -714,7 +714,7 @@ "source": "https://github.com/rockettheme/toolbox/tree/develop", "issues": "https://github.com/rockettheme/toolbox/issues" }, - "time": "2015-11-13 11:39:58" + "time": "2015-11-19 15:03:27" }, { "name": "symfony/console", From 3033818589b87f1257b49e297a47e720f00ebc78 Mon Sep 17 00:00:00 2001 From: Matias Griese Date: Fri, 20 Nov 2015 09:22:07 +0200 Subject: [PATCH 29/79] On plaintext authentication verify, use default hash even if its not set in configuration --- system/src/Grav/Common/User/User.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/system/src/Grav/Common/User/User.php b/system/src/Grav/Common/User/User.php index 2dc37fd7c..b79e94c01 100644 --- a/system/src/Grav/Common/User/User.php +++ b/system/src/Grav/Common/User/User.php @@ -83,7 +83,10 @@ public function authenticate($password) // Plain-text passwords do not match, we know we should fail but execute // verify to protect us from timing attacks and return false regardless of // the result - Authentication::verify($password, self::getGrav()['config']->get('system.security.default_hash')); + Authentication::verify( + $password, + self::getGrav()['config']->get('system.security.default_hash', '$2y$10$kwsyMVwM8/7j0K/6LHT.g.Fs49xOCTp2b8hh/S5.dPJuJcJB6T.UK') + ); return false; } else { // Plain-text does match, we can update the hash and proceed From 268714863e24a1dbabfbc8172549986e86ee65b3 Mon Sep 17 00:00:00 2001 From: Ben Lee Date: Fri, 20 Nov 2015 19:33:24 -0700 Subject: [PATCH 30/79] Prevent crawling of unnecessary directories Prevent search engines from crawling and indexing unnecessary files and directories. The "/user/plugins/" directory may need to be added to the Allow list if plugins use frontend accessible assets. This is tested at a basic level using Google Fetch and Render. --- robots.txt | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/robots.txt b/robots.txt index eb0536286..3b558d635 100644 --- a/robots.txt +++ b/robots.txt @@ -1,2 +1,11 @@ User-agent: * -Disallow: +Disallow: /backup/ +Disallow: /bin/ +Disallow: /cache/ +Disallow: /grav/ +Disallow: /logs/ +Disallow: /system/ +Disallow: /vendor/ +Disallow: /user/ +Allow: /user/pages/ +Allow: /user/themes/ From 2a02c8bc4f00b9acb48a8ca485decb93d6813cdb Mon Sep 17 00:00:00 2001 From: Tamas Csizmadia Date: Sat, 21 Nov 2015 21:53:03 +0100 Subject: [PATCH 31/79] Hungarian translation in system/languages/hu.yaml --- system/languages/hu.yaml | 52 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 system/languages/hu.yaml diff --git a/system/languages/hu.yaml b/system/languages/hu.yaml new file mode 100644 index 000000000..1a77d9e65 --- /dev/null +++ b/system/languages/hu.yaml @@ -0,0 +1,52 @@ +FRONTMATTER_ERROR_PAGE: "---\ncím: %1$s\n---\n\n# Hiba: Érvénytelen Frontmatter\n\nElérési út: `%2$s`\n\n**%3$s**\n\n```\n%4$s\n```" +INFLECTOR_IRREGULAR: + 'person': 'személyek' + 'man': 'férfiak' + 'child': 'gyerekek' + 'sex': 'nemek' + 'move': 'lépések' +INFLECTOR_ORDINALS: + 'default': '.' + 'first': '.' + 'second': '.' + 'third': '.' +NICETIME: + NO_DATE_PROVIDED: Nincs dátum megadva + BAD_DATE: Hibás dátum + AGO: óta + FROM_NOW: mostantól + SECOND: másodperc + MINUTE: perc + HOUR: óra + DAY: nap + WEEK: hét + MONTH: hónap + YEAR: év + DECADE: évtized + SEC: mp + MIN: p + HR: ó + DAY: nap + WK: hét + MO: hó + YR: év + DEC: évt + SECOND_PLURAL: másodperc + MINUTE_PLURAL: perc + HOUR_PLURAL: óra + DAY_PLURAL: nap + WEEK_PLURAL: hét + MONTH_PLURAL: hónap + YEAR_PLURAL: év + DECADE_PLURAL: évtized + SEC_PLURAL: mp + MIN_PLURAL: perc + HR_PLURAL: ó + DAY_PLURAL: nap + WK_PLURAL: hét + MO_PLURAL: hó + YR_PLURAL: év + DEC_PLURAL: évt +FORM: + VALIDATION_FAIL: A validáció hibát talált: + INVALID_INPUT: Az itt megadott érték érvénytelen: From 745b418cd7557c1f87a4df3782c895b26a9a73f6 Mon Sep 17 00:00:00 2001 From: Marc-Antoine Thevenet Date: Sat, 21 Nov 2015 21:47:21 -0400 Subject: [PATCH 32/79] Update fr.yaml update based on the version: en Nov 17, 2015 --- system/languages/fr.yaml | 67 +++++++++++++++++++++++++++++++--------- 1 file changed, 52 insertions(+), 15 deletions(-) diff --git a/system/languages/fr.yaml b/system/languages/fr.yaml index bfc1c117f..dbed71270 100644 --- a/system/languages/fr.yaml +++ b/system/languages/fr.yaml @@ -1,26 +1,60 @@ +FRONTMATTER_ERROR_PAGE: "---\ntitle: %1$s\n---\n\n# Erreur : Frontmatter invalide\n\nPath: `%2$s`\n\n**%3$s**\n\n```\n%4$s\n```" INFLECTOR_PLURALS: - '/$/': 's' - '/(bijou|caillou|chou|genou|hibou|joujou|pou|au|eu|eau)$/': '\1x' - '/(bleu|émeu|landau|lieu|pneu|sarrau)$/': '\1s' - '/(b|cor|ém|gemm|soupir|trav|vant|vitr)ail$/': '\1aux' - '/(s|x|z)$/': '\1' - '/ail$/': 'ails' - '/al$/': 'aux' + '/(quiz)$/i': '\1zes' + '/^(ox)$/i': '\1en' + '/([m|l])ouse$/i': '\1ice' + '/(matr|vert|ind)ix|ex$/i': '\1ices' + '/(x|ch|ss|sh)$/i': '\1es' + '/([^aeiouy]|qu)ies$/i': '\1y' + '/([^aeiouy]|qu)y$/i': '\1ies' + '/(hive)$/i': '\1s' + '/(?:([^f])fe|([lr])f)$/i': '\1\2ves' + '/sis$/i': 'ses' + '/([ti])um$/i': '\1a' + '/(buffal|tomat)o$/i': '\1oes' + '/(bu)s$/i': '\1ses' + '/(alias|status)/i': '\1es' + '/(octop|vir)us$/i': '\1i' + '/(ax|test)is$/i': '\1es' '/s$/i': 's' + '/$/': 's' INFLECTOR_SINGULAR: - '/(bijou|caillou|chou|genou|hibou|joujou|pou|au|eu|eau)x$/': '\1' - '/(b|cor|ém|gemm|soupir|trav|vant|vitr)aux$/': '\1ail' - '/(journ|chev)aux$/': '\1al' - '/ails$/': 'ail' + '/(quiz)zes$/i': '\1' + '/(matr)ices$/i': '\1ix' + '/(vert|ind)ices$/i': '\1ex' + '/^(ox)en/i': '\1' + '/(alias|status)es$/i': '\1' + '/([octop|vir])i$/i': '\1us' + '/(cris|ax|test)es$/i': '\1is' + '/(shoe)s$/i': '\1' + '/(o)es$/i': '\1' + '/(bus)es$/i': '\1' + '/([m|l])ice$/i': '\1ouse' + '/(x|ch|ss|sh)es$/i': '\1' + '/(m)ovies$/i': '\1ovie' + '/(s)eries$/i': '\1eries' + '/([^aeiouy]|qu)ies$/i': '\1y' + '/([lr])ves$/i': '\1f' + '/(tive)s$/i': '\1' + '/(hive)s$/i': '\1' + '/([^f])ves$/i': '\1fe' + '/(^analy)ses$/i': '\1sis' + '/((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$/i': '\1\2sis' + '/([ti])a$/i': '\1um' + '/(n)ews$/i': '\1ews' '/s$/i': '' +INFLECTOR_UNCOUNTABLE: ['équipment', 'information', 'riz', 'argent', 'espèces', 'séries', 'poisson', 'mouton'] INFLECTOR_IRREGULAR: - 'madame': 'mesdames' - 'mademoiselle': 'mesdemoiselles' - 'monsieur': 'messieurs' + 'person': 'personnes' + 'man': 'Hommes' + 'child': 'enfants' + 'sex': 'sexes' + 'move': 'déplacemements' INFLECTOR_ORDINALS: 'default': 'ème' 'first': 'er' 'second': 'nd' + 'third': 'ème' NICETIME: NO_DATE_PROVIDED: Aucune date BAD_DATE: Date erronée @@ -48,7 +82,7 @@ NICETIME: DAY_PLURAL: jours WEEK_PLURAL: semaines MONTH_PLURAL: mois - YEAR_PLURAL: ans + YEAR_PLURAL: années DECADE_PLURAL: décennies SEC_PLURAL: s MIN_PLURAL: m @@ -58,3 +92,6 @@ NICETIME: MO_PLURAL: m YR_PLURAL: a DEC_PLURAL: d +FORM: + VALIDATION_FAIL: La validation a échoué : + INVALID_INPUT: Saisie non valide From 37035a488dcbb24b6e98faf472130a50770c0336 Mon Sep 17 00:00:00 2001 From: Djamil Legato Date: Sat, 21 Nov 2015 20:53:11 -0800 Subject: [PATCH 33/79] Fixed `bin/plugin` help output --- CHANGELOG.md | 10 ++++++++-- bin/plugin | 6 +++--- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 764aff8f5..8d82757aa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +# v1.0.0-rc.6 +## XX/XX/2015 + +1. [](#bugfix) + * Fixed help output for `bin/plugin` + # v1.0.0-rc.5 ## 11/20/2015 @@ -6,7 +12,7 @@ * Implemented the ability for Plugins to provide their own CLI commands through `bin/plugin` * Added Croatian translation * Added missing `umask_fix` property to `system.yaml` - * Added current theme's config to global config. E.g. `config.theme.dropdown_enabled` + * Added current theme's config to global config. E.g. `config.theme.dropdown_enabled` * Added `append_url_extension` option to system config & page headers * Users have a new `state` property to allow disabling/banning * Added new `Page.relativePagePath()` helper method @@ -78,7 +84,7 @@ * German language improvements * Updated bundled composer 1. [](#bugfix) - * Accept variety of `true` values in `User.authorize()` method + * Accept variety of `true` values in `User.authorize()` method * Fix for `Validation` throwing an error if no label set # v1.0.0-rc.1 diff --git a/bin/plugin b/bin/plugin index dc63f4a9d..ffe2dd81f 100755 --- a/bin/plugin +++ b/bin/plugin @@ -44,7 +44,7 @@ $grav['plugins']->init(); $grav['themes']->init(); $app = new Application('Grav Plugins Commands', GRAV_VERSION); -$pattern = '/([A-Z]\w+Command\.php)$/usm'; +$pattern = '([A-Z]\w+Command\.php)'; // get arguments and strip the application name if (null === $argv) { @@ -70,7 +70,7 @@ if (!$name) { $output->writeln(''); $output->writeln("Example:"); $output->writeln(" {$bin} error log -l 1 --trace"); - $list = Folder::all('plugins://', ['compare' => 'Pathname', 'pattern' => '\/cli\/' . $pattern]); + $list = Folder::all('plugins://', ['compare' => 'Pathname', 'pattern' => '/\/cli\/' . $pattern . '$/usm']); if (count($list)) { $available = []; @@ -101,7 +101,7 @@ if ($plugin === null) { $path = 'plugins://' . $name . '/cli'; try { - $commands = Folder::all($path, ['compare' => 'Filename', 'pattern' => $pattern]); + $commands = Folder::all($path, ['compare' => 'Filename', 'pattern' => '/' . $pattern . '$/usm']); } catch (\RuntimeException $e) { $output->writeln("No Console Commands for '{$name}' where found in '{$path}'"); exit; From b29d79738b04897dc1aaed37f3d8f27226361745 Mon Sep 17 00:00:00 2001 From: Tamas Csizmadia Date: Sun, 22 Nov 2015 12:34:44 +0100 Subject: [PATCH 34/79] Updated system/languages/hu.yaml Tweaked translation for 'ago' - sounds less weird. --- system/languages/hu.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system/languages/hu.yaml b/system/languages/hu.yaml index 1a77d9e65..71c0ee91e 100644 --- a/system/languages/hu.yaml +++ b/system/languages/hu.yaml @@ -13,7 +13,7 @@ INFLECTOR_ORDINALS: NICETIME: NO_DATE_PROVIDED: Nincs dátum megadva BAD_DATE: Hibás dátum - AGO: óta + AGO: elteltével FROM_NOW: mostantól SECOND: másodperc MINUTE: perc From 1f5df814966fd13dc9afe64d8e057ffd5de9136b Mon Sep 17 00:00:00 2001 From: Matias Griese Date: Mon, 23 Nov 2015 11:00:03 +0200 Subject: [PATCH 35/79] Sync Folder class with Gantry --- system/src/Grav/Common/Filesystem/Folder.php | 140 ++++++++++++++----- 1 file changed, 106 insertions(+), 34 deletions(-) diff --git a/system/src/Grav/Common/Filesystem/Folder.php b/system/src/Grav/Common/Filesystem/Folder.php index af1b93352..62d610b65 100644 --- a/system/src/Grav/Common/Filesystem/Folder.php +++ b/system/src/Grav/Common/Filesystem/Folder.php @@ -19,12 +19,12 @@ public static function lastModifiedFolder($path) { $last_modified = 0; - $dirItr = new \RecursiveDirectoryIterator($path, \RecursiveDirectoryIterator::SKIP_DOTS); - $filterItr = new RecursiveFolderFilterIterator($dirItr); - $itr = new \RecursiveIteratorIterator($filterItr, \RecursiveIteratorIterator::SELF_FIRST); + $directory = new \RecursiveDirectoryIterator($path, \RecursiveDirectoryIterator::SKIP_DOTS); + $filter = new RecursiveFolderFilterIterator($directory); + $iterator = new \RecursiveIteratorIterator($filter, \RecursiveIteratorIterator::SELF_FIRST); /** @var \RecursiveDirectoryIterator $file */ - foreach ($itr as $dir) { + foreach ($iterator as $dir) { $dir_modified = $dir->getMTime(); if ($dir_modified > $last_modified) { $last_modified = $dir_modified; @@ -46,12 +46,12 @@ public static function lastModifiedFile($path, $extensions = 'md|yaml') { $last_modified = 0; - $dirItr = new \RecursiveDirectoryIterator($path, \RecursiveDirectoryIterator::SKIP_DOTS); - $itrItr = new \RecursiveIteratorIterator($dirItr, \RecursiveIteratorIterator::SELF_FIRST); - $itr = new \RegexIterator($itrItr, '/^.+\.'.$extensions.'$/i'); + $directory = new \RecursiveDirectoryIterator($path, \RecursiveDirectoryIterator::SKIP_DOTS); + $recursive = new \RecursiveIteratorIterator($directory, \RecursiveIteratorIterator::SELF_FIRST); + $iterator = new \RegexIterator($recursive, '/^.+\.'.$extensions.'$/i'); /** @var \RecursiveDirectoryIterator $file */ - foreach ($itr as $filepath => $file) { + foreach ($iterator as $filepath => $file) { $file_modified = $file->getMTime(); if ($file_modified > $last_modified) { $last_modified = $file_modified; @@ -64,8 +64,8 @@ public static function lastModifiedFile($path, $extensions = 'md|yaml') /** * Get relative path between target and base path. If path isn't relative, return full path. * - * @param string $path - * @param mixed|string $base + * @param string $path + * @param string $base * @return string */ public static function getRelativePath($path, $base = GRAV_ROOT) @@ -81,6 +81,43 @@ public static function getRelativePath($path, $base = GRAV_ROOT) return $path; } + /** + * Get relative path between target and base path. If path isn't relative, return full path. + * + * @param string $path + * @param string $base + * @return string + */ + public static function getRelativePathDotDot($path, $base) + { + $base = preg_replace('![\\\/]+!', '/', $base); + $path = preg_replace('![\\\/]+!', '/', $path); + + if ($path === $base) { + return ''; + } + + $baseParts = explode('/', isset($base[0]) && '/' === $base[0] ? substr($base, 1) : $base); + $pathParts = explode('/', isset($path[0]) && '/' === $path[0] ? substr($path, 1) : $path); + + array_pop($baseParts); + $lastPart = array_pop($pathParts); + foreach ($baseParts as $i => $directory) { + if (isset($pathParts[$i]) && $pathParts[$i] === $directory) { + unset($baseParts[$i], $pathParts[$i]); + } else { + break; + } + } + $pathParts[] = $lastPart; + $path = str_repeat('../', count($baseParts)) . implode('/', $pathParts); + + return '' === $path + || '/' === $path[0] + || false !== ($colonPos = strpos($path, ':')) && ($colonPos < ($slashPos = strpos($path, '/')) || false === $slashPos) + ? "./$path" : $path; + } + /** * Shift first directory out of the path. * @@ -96,8 +133,6 @@ public static function shift(&$path) return $result ?: null; } - - /** * Return recursive list of all files and directories under given path. * @@ -116,13 +151,17 @@ public static function all($path, array $params = array()) $pattern = isset($params['pattern']) ? $params['pattern'] : null; $filters = isset($params['filters']) ? $params['filters'] : null; $recursive = isset($params['recursive']) ? $params['recursive'] : true; + $levels = isset($params['levels']) ? $params['levels'] : -1; $key = isset($params['key']) ? 'get' . $params['key'] : null; $value = isset($params['value']) ? 'get' . $params['value'] : ($recursive ? 'getSubPathname' : 'getFilename'); + $folders = isset($params['folders']) ? $params['folders'] : true; + $files = isset($params['files']) ? $params['files'] : true; if ($recursive) { $directory = new \RecursiveDirectoryIterator($path, \RecursiveDirectoryIterator::SKIP_DOTS + \FilesystemIterator::UNIX_PATHS + \FilesystemIterator::CURRENT_AS_SELF); $iterator = new \RecursiveIteratorIterator($directory, \RecursiveIteratorIterator::SELF_FIRST); + $iterator->setMaxDepth(max($levels, -1)); } else { $iterator = new \FilesystemIterator($path); } @@ -131,6 +170,16 @@ public static function all($path, array $params = array()) /** @var \RecursiveDirectoryIterator $file */ foreach ($iterator as $file) { + // Ignore hidden files. + if ($file->getFilename()[0] == '.') { + continue; + } + if (!$folders && $file->isDir()) { + continue; + } + if (!$files && $file->isFile()) { + continue; + } if ($compare && $pattern && !preg_match($pattern, $file->{$compare}())) { continue; } @@ -138,7 +187,8 @@ public static function all($path, array $params = array()) $filePath = $file->{$value}(); if ($filters) { if (isset($filters['key'])) { - $fileKey = preg_replace($filters['key'], '', $fileKey); + $pre = !empty($filters['pre-key']) ? $filters['pre-key'] : ''; + $fileKey = $pre . preg_replace($filters['key'], '', $fileKey); } if (isset($filters['value'])) { $filter = $filters['value']; @@ -146,12 +196,12 @@ public static function all($path, array $params = array()) $filePath = call_user_func($filter, $file); } else { $filePath = preg_replace($filter, '', $filePath); + } } } - } if ($fileKey !== null) { - $results[$fileKey] = $filePath; + $results[$fileKey] = $filePath; } else { $results[] = $filePath; } @@ -163,11 +213,12 @@ public static function all($path, array $params = array()) /** * Recursively copy directory in filesystem. * - * @param string $source - * @param string $target + * @param string $source + * @param string $target + * @param string $ignore Ignore files matching pattern (regular expression). * @throws \RuntimeException */ - public static function copy($source, $target) + public static function copy($source, $target, $ignore = null) { $source = rtrim($source, '\\/'); $target = rtrim($target, '\\/'); @@ -177,19 +228,24 @@ public static function copy($source, $target) } // Make sure that path to the target exists before copying. - self::mkdir($target); + self::create($target); $success = true; // Go through all sub-directories and copy everything. $files = self::all($source); foreach ($files as $file) { + if ($ignore && preg_match($ignore, $file)) { + continue; + } $src = $source .'/'. $file; $dst = $target .'/'. $file; if (is_dir($src)) { - // Create current directory. - $success &= @mkdir($dst); + // Create current directory (if it doesn't exist). + if (!is_dir($dst)) { + $success &= @mkdir($dst, 0777, true); + } } else { // Or copy current file. $success &= @copy($src, $dst); @@ -208,8 +264,8 @@ public static function copy($source, $target) /** * Move directory in filesystem. * - * @param string $source - * @param string $target + * @param string $source + * @param string $target * @throws \RuntimeException */ public static function move($source, $target) @@ -219,7 +275,7 @@ public static function move($source, $target) } // Make sure that path to the target exists before moving. - self::mkdir(dirname($target)); + self::create(dirname($target)); // Just rename the directory. $success = @rename($source, $target); @@ -238,16 +294,16 @@ public static function move($source, $target) * Recursively delete directory from filesystem. * * @param string $target - * @throws \RuntimeException + * @param bool $include_target * @return bool */ - public static function delete($target) + public static function delete($target, $include_target = true) { if (!is_dir($target)) { - return; + return false; } - $success = self::doDelete($target); + $success = self::doDelete($target, $include_target); if (!$success) { $error = error_get_last(); @@ -255,16 +311,31 @@ public static function delete($target) } // Make sure that the change will be detected when caching. - @touch(dirname($target)); + if ($include_target) { + @touch(dirname($target)); + } else { + @touch($target); + } + return $success; } /** - * @param string $folder + * @param string $folder * @throws \RuntimeException * @internal */ public static function mkdir($folder) + { + self::create($folder); + } + + /** + * @param string $folder + * @throws \RuntimeException + * @internal + */ + public static function create($folder) { if (is_dir($folder)) { return; @@ -320,10 +391,11 @@ public static function rcopy($src, $dest) /** * @param string $folder + * @param bool $include_target * @return bool * @internal */ - protected static function doDelete($folder) + protected static function doDelete($folder, $include_target = true) { // Special case for symbolic links. if (is_link($folder)) { @@ -338,16 +410,16 @@ protected static function doDelete($folder) /** @var \DirectoryIterator $fileinfo */ foreach ($files as $fileinfo) { if ($fileinfo->isDir()) { - if (false === rmdir($fileinfo->getRealPath())) { + if (false === @rmdir($fileinfo->getRealPath())) { return false; } } else { - if (false === unlink($fileinfo->getRealPath())) { + if (false === @unlink($fileinfo->getRealPath())) { return false; } } } - return rmdir($folder); + return $include_target ? @rmdir($folder) : true; } } From 9764cf3f65fd1708cf242d886ed78342cbc82f07 Mon Sep 17 00:00:00 2001 From: Matias Griese Date: Mon, 23 Nov 2015 11:00:28 +0200 Subject: [PATCH 36/79] Sync CompiledFile class with Gantry --- system/src/Grav/Common/File/CompiledFile.php | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/system/src/Grav/Common/File/CompiledFile.php b/system/src/Grav/Common/File/CompiledFile.php index 7120c9c30..6b6b76989 100644 --- a/system/src/Grav/Common/File/CompiledFile.php +++ b/system/src/Grav/Common/File/CompiledFile.php @@ -47,7 +47,11 @@ public function content($var = null) || $cache['filename'] != $this->filename ) { // Attempt to lock the file for writing. - $file->lock(false); + try { + $file->lock(false); + } catch (\Exception $e) { + // Another process has locked the file; we will check this in a bit. + } // Decode RAW file into compiled array. $data = (array) $this->decode($this->raw()); @@ -64,6 +68,7 @@ public function content($var = null) $file->unlock(); } } + $file->free(); $this->content = $cache['data']; } From 1d97f98515d5d8a283c8350269580c9a1f1ebe5d Mon Sep 17 00:00:00 2001 From: Andy Miller Date: Mon, 23 Nov 2015 17:41:16 -0700 Subject: [PATCH 37/79] Made hash() public for future usage --- system/src/Grav/Common/Utils.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system/src/Grav/Common/Utils.php b/system/src/Grav/Common/Utils.php index 515d05851..12c3d1026 100644 --- a/system/src/Grav/Common/Utils.php +++ b/system/src/Grav/Common/Utils.php @@ -474,7 +474,7 @@ private static function nonceTick() * * @return string hashed value of $data, cut to 10 characters */ - private static function hash($data) + public static function hash($data) { $hash = password_hash($data, PASSWORD_DEFAULT); return $hash; From b8f00243e6c18b0305e8ef1ba0e888043dc09b8b Mon Sep 17 00:00:00 2001 From: Andy Miller Date: Mon, 23 Nov 2015 19:07:12 -0700 Subject: [PATCH 38/79] Make hash() public for future use --- system/src/Grav/Common/Utils.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system/src/Grav/Common/Utils.php b/system/src/Grav/Common/Utils.php index 515d05851..12c3d1026 100644 --- a/system/src/Grav/Common/Utils.php +++ b/system/src/Grav/Common/Utils.php @@ -474,7 +474,7 @@ private static function nonceTick() * * @return string hashed value of $data, cut to 10 characters */ - private static function hash($data) + public static function hash($data) { $hash = password_hash($data, PASSWORD_DEFAULT); return $hash; From 965c8cfbe9dfafbabe12036032aef5a543713a9c Mon Sep 17 00:00:00 2001 From: Andy Miller Date: Mon, 23 Nov 2015 19:07:46 -0700 Subject: [PATCH 39/79] Only generate one nonce per process --- system/src/Grav/Common/Utils.php | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/system/src/Grav/Common/Utils.php b/system/src/Grav/Common/Utils.php index 12c3d1026..71a706791 100644 --- a/system/src/Grav/Common/Utils.php +++ b/system/src/Grav/Common/Utils.php @@ -15,6 +15,8 @@ abstract class Utils { use GravTrait; + protected static $nonce; + /** * @param string $haystack * @param string $needle @@ -491,9 +493,15 @@ public static function hash($data) */ public static function getNonce($action, $plusOneTick = false) { + // Don't regenerate this again if not needed + if (isset(static::$nonce)) { + return static::$nonce; + } $nonce = self::hash(self::generateNonceString($action, $plusOneTick)); - $nonce = str_replace('/', 'SLASH', $nonce); - return $nonce; + + static::$nonce = str_replace('/', 'SLASH', $nonce); + + return static::$nonce; } /** From 583156d2f3fd54c073f17e5af771da7e2a0c55cf Mon Sep 17 00:00:00 2001 From: Flavio Copes Date: Tue, 24 Nov 2015 09:03:48 +0100 Subject: [PATCH 40/79] Handle multiple nonce action types per page --- system/src/Grav/Common/Utils.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/system/src/Grav/Common/Utils.php b/system/src/Grav/Common/Utils.php index 71a706791..39528220d 100644 --- a/system/src/Grav/Common/Utils.php +++ b/system/src/Grav/Common/Utils.php @@ -15,7 +15,7 @@ abstract class Utils { use GravTrait; - protected static $nonce; + protected static $nonces = []; /** * @param string $haystack @@ -494,14 +494,14 @@ public static function hash($data) public static function getNonce($action, $plusOneTick = false) { // Don't regenerate this again if not needed - if (isset(static::$nonce)) { - return static::$nonce; + if (isset(static::$nonces[$action])) { + return static::$nonces[$action]; } $nonce = self::hash(self::generateNonceString($action, $plusOneTick)); - static::$nonce = str_replace('/', 'SLASH', $nonce); + static::$nonces[$action] = str_replace('/', 'SLASH', $nonce); - return static::$nonce; + return static::$nonces[$action]; } /** From 20f17130a2ec5af50866ee86975e3d48d04cc26f Mon Sep 17 00:00:00 2001 From: Flavio Copes Date: Tue, 24 Nov 2015 15:17:08 +0100 Subject: [PATCH 41/79] Add the IP address of the user to the nonce string calculation even if it's logged in --- system/src/Grav/Common/Utils.php | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/system/src/Grav/Common/Utils.php b/system/src/Grav/Common/Utils.php index 39528220d..759d3f949 100644 --- a/system/src/Grav/Common/Utils.php +++ b/system/src/Grav/Common/Utils.php @@ -437,11 +437,10 @@ private static function generateNonceString($action, $plusOneTick = false) if (isset(self::getGrav()['user'])) { $user = self::getGrav()['user']; $username = $user->username; + if (isset($_SERVER['REMOTE_ADDR'])) { + $username .= $_SERVER['REMOTE_ADDR']; + } } else { - $username = false; - } - - if (!$username) { $username = isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : ''; } From e00560f81a72eb0429d1a638577c9727a30d449c Mon Sep 17 00:00:00 2001 From: Flavio Copes Date: Tue, 24 Nov 2015 15:18:10 +0100 Subject: [PATCH 42/79] Handle languages that support _PLURAL_MORE_THAN_TWO Ex. WEEK_PLURAL, WEEK_PLURAL_MORE_THAN_TWO --- system/src/Grav/Common/Twig/TwigExtension.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/system/src/Grav/Common/Twig/TwigExtension.php b/system/src/Grav/Common/Twig/TwigExtension.php index 4ea09e350..12a1cb633 100644 --- a/system/src/Grav/Common/Twig/TwigExtension.php +++ b/system/src/Grav/Common/Twig/TwigExtension.php @@ -355,6 +355,12 @@ public function nicetimeFilter($date, $long_strings = true) $periods[$j] .= '_PLURAL'; } + if ($this->grav['language']->getTranslation($this->grav['language']->getLanguage(), $periods[$j] . '_MORE_THAN_TWO')) { + if ($difference > 2) { + $periods[$j] .= '_MORE_THAN_TWO'; + } + } + $periods[$j] = $this->grav['language']->translate($periods[$j], null, true); return "$difference $periods[$j] {$tense}"; From bdd17fc56af2ce145de3ff37313992d4a1c3b8af Mon Sep 17 00:00:00 2001 From: Andy Miller Date: Tue, 24 Nov 2015 10:06:41 -0700 Subject: [PATCH 43/79] Moving back to release version of Toolbox --- composer.json | 8 +---- composer.lock | 92 ++++++++++++++++++++++++++++----------------------- 2 files changed, 51 insertions(+), 49 deletions(-) diff --git a/composer.json b/composer.json index a4a62a98e..86116ed1f 100644 --- a/composer.json +++ b/composer.json @@ -21,15 +21,9 @@ "mrclay/minify": "~2.2", "donatj/phpuseragentparser": "~0.3", "pimple/pimple": "~3.0", - "rockettheme/toolbox": "dev-develop", + "rockettheme/toolbox": "~1.2", "maximebf/debugbar": "~1.10" }, - "repositories": [ - { - "type": "vcs", - "url": "https://github.com/rockettheme/toolbox" - } - ], "autoload": { "psr-4": { "Grav\\": "system/src/Grav" diff --git a/composer.lock b/composer.lock index c19bcb526..25c0429a7 100644 --- a/composer.lock +++ b/composer.lock @@ -4,8 +4,8 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "hash": "3633e92a6e340a26229689d9b74031c2", - "content-hash": "9c314f997d29d7a78d3cf14f5976e6df", + "hash": "b1323e540382de7390663756b3a87de7", + "content-hash": "158cd4e3e1a40d5d1db9e3f29c24d83a", "packages": [ { "name": "doctrine/cache", @@ -169,16 +169,16 @@ }, { "name": "erusev/parsedown-extra", - "version": "0.7.0", + "version": "0.7.1", "source": { "type": "git", "url": "https://github.com/erusev/parsedown-extra.git", - "reference": "11a44e076d02ffcc4021713398a60cd73f78b6f5" + "reference": "0db5cce7354e4b76f155d092ab5eb3981c21258c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/erusev/parsedown-extra/zipball/11a44e076d02ffcc4021713398a60cd73f78b6f5", - "reference": "11a44e076d02ffcc4021713398a60cd73f78b6f5", + "url": "https://api.github.com/repos/erusev/parsedown-extra/zipball/0db5cce7354e4b76f155d092ab5eb3981c21258c", + "reference": "0db5cce7354e4b76f155d092ab5eb3981c21258c", "shasum": "" }, "require": { @@ -209,7 +209,7 @@ "parsedown", "parser" ], - "time": "2015-01-25 14:52:34" + "time": "2015-11-01 10:19:22" }, { "name": "filp/whoops", @@ -666,16 +666,16 @@ }, { "name": "rockettheme/toolbox", - "version": "dev-develop", + "version": "1.2.0", "source": { "type": "git", "url": "https://github.com/rockettheme/toolbox.git", - "reference": "defdf78a4c2f537067c6548c99560b78ed92745d" + "reference": "0c7a3b4b6e4d73be8512e89f7acde6899334b7f2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/rockettheme/toolbox/zipball/defdf78a4c2f537067c6548c99560b78ed92745d", - "reference": "defdf78a4c2f537067c6548c99560b78ed92745d", + "url": "https://api.github.com/repos/rockettheme/toolbox/zipball/0c7a3b4b6e4d73be8512e89f7acde6899334b7f2", + "reference": "0c7a3b4b6e4d73be8512e89f7acde6899334b7f2", "shasum": "" }, "require": { @@ -701,6 +701,7 @@ "RocketTheme\\Toolbox\\StreamWrapper\\": "StreamWrapper/src" } }, + "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], @@ -710,24 +711,20 @@ "php", "rockettheme" ], - "support": { - "source": "https://github.com/rockettheme/toolbox/tree/develop", - "issues": "https://github.com/rockettheme/toolbox/issues" - }, - "time": "2015-11-19 15:03:27" + "time": "2015-11-24 17:04:24" }, { "name": "symfony/console", - "version": "v2.7.6", + "version": "v2.7.7", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "5efd632294c8320ea52492db22292ff853a43766" + "reference": "16bb1cb86df43c90931df65f529e7ebd79636750" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/5efd632294c8320ea52492db22292ff853a43766", - "reference": "5efd632294c8320ea52492db22292ff853a43766", + "url": "https://api.github.com/repos/symfony/console/zipball/16bb1cb86df43c90931df65f529e7ebd79636750", + "reference": "16bb1cb86df43c90931df65f529e7ebd79636750", "shasum": "" }, "require": { @@ -752,7 +749,10 @@ "autoload": { "psr-4": { "Symfony\\Component\\Console\\": "" - } + }, + "exclude-from-classmap": [ + "/Tests/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -770,20 +770,20 @@ ], "description": "Symfony Console Component", "homepage": "https://symfony.com", - "time": "2015-10-20 14:38:46" + "time": "2015-11-18 09:54:26" }, { "name": "symfony/event-dispatcher", - "version": "v2.7.6", + "version": "v2.7.7", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "87a5db5ea887763fa3a31a5471b512ff1596d9b8" + "reference": "7e2f9c31645680026c2372edf66f863fc7757af5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/87a5db5ea887763fa3a31a5471b512ff1596d9b8", - "reference": "87a5db5ea887763fa3a31a5471b512ff1596d9b8", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/7e2f9c31645680026c2372edf66f863fc7757af5", + "reference": "7e2f9c31645680026c2372edf66f863fc7757af5", "shasum": "" }, "require": { @@ -809,7 +809,10 @@ "autoload": { "psr-4": { "Symfony\\Component\\EventDispatcher\\": "" - } + }, + "exclude-from-classmap": [ + "/Tests/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -827,20 +830,20 @@ ], "description": "Symfony EventDispatcher Component", "homepage": "https://symfony.com", - "time": "2015-10-11 09:39:48" + "time": "2015-10-30 20:10:21" }, { "name": "symfony/var-dumper", - "version": "v2.7.6", + "version": "v2.7.7", "source": { "type": "git", "url": "https://github.com/symfony/var-dumper.git", - "reference": "eb033050050916b6bfa51be71009ef67b16046c9" + "reference": "72bcb27411780eaee9469729aace73c0d46fb2b8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-dumper/zipball/eb033050050916b6bfa51be71009ef67b16046c9", - "reference": "eb033050050916b6bfa51be71009ef67b16046c9", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/72bcb27411780eaee9469729aace73c0d46fb2b8", + "reference": "72bcb27411780eaee9469729aace73c0d46fb2b8", "shasum": "" }, "require": { @@ -861,7 +864,10 @@ ], "psr-4": { "Symfony\\Component\\VarDumper\\": "" - } + }, + "exclude-from-classmap": [ + "/Tests/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -883,20 +889,20 @@ "debug", "dump" ], - "time": "2015-10-25 17:17:38" + "time": "2015-11-18 13:41:01" }, { "name": "symfony/yaml", - "version": "v2.7.6", + "version": "v2.7.7", "source": { "type": "git", "url": "https://github.com/symfony/yaml.git", - "reference": "eca9019c88fbe250164affd107bc8057771f3f4d" + "reference": "4cfcd7a9fceba662b3c036b7d9a91f6197af046c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/eca9019c88fbe250164affd107bc8057771f3f4d", - "reference": "eca9019c88fbe250164affd107bc8057771f3f4d", + "url": "https://api.github.com/repos/symfony/yaml/zipball/4cfcd7a9fceba662b3c036b7d9a91f6197af046c", + "reference": "4cfcd7a9fceba662b3c036b7d9a91f6197af046c", "shasum": "" }, "require": { @@ -911,7 +917,10 @@ "autoload": { "psr-4": { "Symfony\\Component\\Yaml\\": "" - } + }, + "exclude-from-classmap": [ + "/Tests/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -929,7 +938,7 @@ ], "description": "Symfony Yaml Component", "homepage": "https://symfony.com", - "time": "2015-10-11 09:39:48" + "time": "2015-11-18 13:41:01" }, { "name": "twig/twig", @@ -997,8 +1006,7 @@ "aliases": [], "minimum-stability": "stable", "stability-flags": { - "filp/whoops": 20, - "rockettheme/toolbox": 20 + "filp/whoops": 20 }, "prefer-stable": false, "prefer-lowest": false, From 385233c508893176be6fbfc388661864b9cefdc7 Mon Sep 17 00:00:00 2001 From: Matias Griese Date: Tue, 24 Nov 2015 21:14:46 +0200 Subject: [PATCH 44/79] Automatically create unique security salt for each configuration --- CHANGELOG.md | 9 +++------ system/src/Grav/Common/Config/Setup.php | 15 +++++++++++++-- 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fb86aede2..ddb6ba57d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,4 @@ -# v1.0.0-refactor +# v1.0.0-rc.6 ## XX/XX/2015 1. [](#new) @@ -6,11 +6,8 @@ * Data objects: Allow function call chaining * Data objects: Lazy load blueprints only if needed * Refactor Config classes - -# v1.0.0-rc.6 -## XX/XX/2015 - -1. [](#bugfix) + * Automatically create unique security salt for each configuration +2. [](#bugfix) * Fixed help output for `bin/plugin` # v1.0.0-rc.5 diff --git a/system/src/Grav/Common/Config/Setup.php b/system/src/Grav/Common/Config/Setup.php index ef89df015..9bb66853c 100644 --- a/system/src/Grav/Common/Config/Setup.php +++ b/system/src/Grav/Common/Config/Setup.php @@ -3,6 +3,8 @@ use Grav\Common\File\CompiledYamlFile; use Grav\Common\Data\Data; +use Grav\Common\Utils; +use RocketTheme\Toolbox\File\YamlFile; use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator; /** @@ -165,7 +167,7 @@ public function init() } // Make sure we have valid setup. - $this->check(); + $this->check($locator); return $this; } @@ -214,9 +216,10 @@ public function getStreams() } /** + * @param UniformResourceLocator $locator * @throws \InvalidArgumentException */ - protected function check() + protected function check(UniformResourceLocator $locator) { $streams = isset($this->items['streams']['schemes']) ? $this->items['streams']['schemes'] : null; if (!is_array($streams)) { @@ -228,5 +231,13 @@ protected function check() sprintf('Configuration is missing keys %s from streams.schemes!', implode(', ', $diff)) ); } + + // Create security.yaml if it doesn't exist. + $filename = $locator->findResource('config://security.yaml', true, true); + $file = YamlFile::instance($filename); + if (!$file->exists()) { + $file->save(['salt' => Utils::generateRandomString(14)]); + $file->free(); + } } } From 34f83ebde2c7529bf2ab33e3cd17e8022aa7096f Mon Sep 17 00:00:00 2001 From: Flavio Copes Date: Tue, 24 Nov 2015 21:13:46 +0100 Subject: [PATCH 45/79] Use the new security salt to calculate the nonce instead of using password_hash --- system/src/Grav/Common/Utils.php | 28 +++++----------------------- 1 file changed, 5 insertions(+), 23 deletions(-) diff --git a/system/src/Grav/Common/Utils.php b/system/src/Grav/Common/Utils.php index 759d3f949..90e79ffa2 100644 --- a/system/src/Grav/Common/Utils.php +++ b/system/src/Grav/Common/Utils.php @@ -423,7 +423,6 @@ public static function isPositive($value) return in_array($value, [true, 1, '1', 'yes', 'on', 'true'], true); } - /** * Generates a nonce string to be hashed. Called by self::getNonce() * @@ -451,7 +450,7 @@ private static function generateNonceString($action, $plusOneTick = false) $i++; } - return ( $i . '|' . $action . '|' . $username . '|' . $token ); + return ( $i . '|' . $action . '|' . $username . '|' . $token . '|' . self::getGrav()['config']->get('security.salt')); } /** @@ -468,19 +467,6 @@ private static function nonceTick() return (int)ceil(time() / ( $secondsInHalfADay )); } - /** - * Get hash of given string - * - * @param string $data string to hash - * - * @return string hashed value of $data, cut to 10 characters - */ - public static function hash($data) - { - $hash = password_hash($data, PASSWORD_DEFAULT); - return $hash; - } - /** * Creates a hashed nonce tied to the passed action. Tied to the current user and time. The nonce for a given * action is the same for 12 hours. @@ -496,9 +482,8 @@ public static function getNonce($action, $plusOneTick = false) if (isset(static::$nonces[$action])) { return static::$nonces[$action]; } - $nonce = self::hash(self::generateNonceString($action, $plusOneTick)); - - static::$nonces[$action] = str_replace('/', 'SLASH', $nonce); + $nonce = md5(self::generateNonceString($action, $plusOneTick)); + static::$nonces[$action] = $nonce; return static::$nonces[$action]; } @@ -513,21 +498,18 @@ public static function getNonce($action, $plusOneTick = false) */ public static function verifyNonce($nonce, $action) { - $nonce = str_replace('SLASH', '/', $nonce); - //Nonce generated 0-12 hours ago - if (password_verify(self::generateNonceString($action), $nonce)) { + if ($nonce == self::getNonce($action)) { return true; } //Nonce generated 12-24 hours ago $plusOneTick = true; - if (password_verify(self::generateNonceString($action, $plusOneTick), $nonce)) { + if ($nonce == self::getNonce($action, $plusOneTick)) { return true; } //Invalid nonce return false; } - } From 13207f13addca3d303fe651f567fb9aaac7936cd Mon Sep 17 00:00:00 2001 From: Djamil Legato Date: Tue, 24 Nov 2015 14:57:05 -0800 Subject: [PATCH 46/79] Fixed nested logic for lists and forms parsing (fixes #273) --- system/src/Grav/Common/Data/Blueprint.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system/src/Grav/Common/Data/Blueprint.php b/system/src/Grav/Common/Data/Blueprint.php index 6f0c71e50..c261ed877 100644 --- a/system/src/Grav/Common/Data/Blueprint.php +++ b/system/src/Grav/Common/Data/Blueprint.php @@ -331,7 +331,7 @@ protected function parseFormFields(array &$fields, $params, $prefix, array &$cur $field['name'] = $prefix . $key; $field += $params; - if (isset($field['fields']) && (!isset($field['type']) || $field['type'] !== 'list')) { + if (isset($field['fields']) && isset($field['type'])) { // Recursively get all the nested fields. $newParams = array_intersect_key($this->filter, $field); $this->parseFormFields($field['fields'], $newParams, $prefix, $current[$key]['fields']); From 9c07d69c4587c551fe988475b7ee3fd6d78c4d3a Mon Sep 17 00:00:00 2001 From: nazwa Date: Tue, 24 Nov 2015 23:02:42 +0000 Subject: [PATCH 47/79] Proper handling of list fields Moved field handling to a separate function and added logic to correctly process nested lists. --- system/src/Grav/Common/Data/Blueprint.php | 192 ++++++++++++---------- 1 file changed, 108 insertions(+), 84 deletions(-) diff --git a/system/src/Grav/Common/Data/Blueprint.php b/system/src/Grav/Common/Data/Blueprint.php index 6f0c71e50..aefd1b75c 100644 --- a/system/src/Grav/Common/Data/Blueprint.php +++ b/system/src/Grav/Common/Data/Blueprint.php @@ -314,91 +314,115 @@ protected function extraArray(array $data, array $rules, $prefix) } /** - * Gets all field definitions from the blueprints. - * - * @param array $fields - * @param array $params - * @param string $prefix - * @param array $current - * @internal - */ - protected function parseFormFields(array &$fields, $params, $prefix, array &$current) - { - // Go though all the fields in current level. - foreach ($fields as $key => &$field) { - $current[$key] = &$field; - // Set name from the array key. - $field['name'] = $prefix . $key; - $field += $params; - - if (isset($field['fields']) && (!isset($field['type']) || $field['type'] !== 'list')) { - // Recursively get all the nested fields. - $newParams = array_intersect_key($this->filter, $field); - $this->parseFormFields($field['fields'], $newParams, $prefix, $current[$key]['fields']); - } else if ($field['type'] !== 'ignore') { - // Add rule. + * Gets all field definitions from the blueprints. + * + * @param array $fields + * @param array $params + * @param string $prefix + * @param array $current + * @internal + */ + protected function parseFormFields(array &$fields, $params, $prefix, array &$current) + { + // Go though all the fields in current level. + foreach ($fields as $key => &$field) { + $current[$key] = &$field; + // Set name from the array key. + $field['name'] = $prefix . $key; + $field += $params; + + if (isset($field['fields']) && (!isset($field['type']) || $field['type'] !== 'list')) { + // Recursively get all the nested fields. + $newParams = array_intersect_key($this->filter, $field); + $this->parseFormFields($field['fields'], $newParams, $prefix, $current[$key]['fields']); + } else if( $field['type'] === 'list') { + // Lists have different structure for fields (one level deeper) + // This means we need to loop through the list to get to the actual field + // The property and rule setting need to be set on the list field + // and NOT on the children, which is why we need to duplicate some of + // the code below :( + + $this->rules[$prefix . $key] = &$field; + $this->addProperty($prefix . $key); + + foreach($field['fields'] as $subName => &$subField) { + $this->parseFormField($subField, $prefix, $key, false); + } + + if (isset($field['validate']['rule']) && $field['type'] !== 'ignore') { + $field['validate'] += $this->getRule($field['validate']['rule']); + } + } else if ($field['type'] !== 'ignore') { $this->rules[$prefix . $key] = &$field; - $this->addProperty($prefix . $key); - - foreach ($field as $name => $value) { - // Support nested blueprints. - if ($this->context && $name == '@import') { - $values = (array) $value; - if (!isset($field['fields'])) { - $field['fields'] = array(); - } - foreach ($values as $bname) { - $b = $this->context->get($bname); - $field['fields'] = array_merge($field['fields'], $b->fields()); - } - } - - // Support for callable data values. - elseif (substr($name, 0, 6) == '@data-') { - $property = substr($name, 6); - if (is_array($value)) { - $func = array_shift($value); - } else { - $func = $value; - $value = array(); - } - list($o, $f) = preg_split('/::/', $func); - if (!$f && function_exists($o)) { - $data = call_user_func_array($o, $value); - } elseif ($f && method_exists($o, $f)) { - $data = call_user_func_array(array($o, $f), $value); - } - - // If function returns a value, - if (isset($data)) { - if (isset($field[$property]) && is_array($field[$property]) && is_array($data)) { - // Combine field and @data-field together. - $field[$property] += $data; - } else { - // Or create/replace field with @data-field. - $field[$property] = $data; - } - } - } - - elseif (substr($name, 0, 8) == '@config-') { - $property = substr($name, 8); - $default = isset($field[$property]) ? $field[$property] : null; - $config = self::getGrav()['config']->get($value, $default); - - if (!is_null($config)) { - $field[$property] = $config; - } - } - } - - // Initialize predefined validation rule. - if (isset($field['validate']['rule']) && $field['type'] !== 'ignore') { - $field['validate'] += $this->getRule($field['validate']['rule']); - } - } - } - } + $this->addProperty($prefix . $key); + + $this->parseFormField($field, $prefix, $key, true); + + if (isset($field['validate']['rule']) && $field['type'] !== 'ignore') { + $field['validate'] += $this->getRule($field['validate']['rule']); + } + } + } + } + /** + * Parses individual field definition + * + * @param array $field + * @internal + */ + protected function parseFormField(&$field) { + foreach ($field as $name => $value) { + // Support nested blueprints. + if ($this->context && $name == '@import') { + $values = (array) $value; + if (!isset($field['fields'])) { + $field['fields'] = array(); + } + foreach ($values as $bname) { + $b = $this->context->get($bname); + $field['fields'] = array_merge($field['fields'], $b->fields()); + } + } + + // Support for callable data values. + elseif (substr($name, 0, 6) == '@data-') { + $property = substr($name, 6); + if (is_array($value)) { + $func = array_shift($value); + } else { + $func = $value; + $value = array(); + } + list($o, $f) = preg_split('/::/', $func); + if (!$f && function_exists($o)) { + $data = call_user_func_array($o, $value); + } elseif ($f && method_exists($o, $f)) { + $data = call_user_func_array(array($o, $f), $value); + } + + // If function returns a value, + if (isset($data)) { + if (isset($field[$property]) && is_array($field[$property]) && is_array($data)) { + // Combine field and @data-field together. + $field[$property] += $data; + } else { + // Or create/replace field with @data-field. + $field[$property] = $data; + } + } + } + + elseif (substr($name, 0, 8) == '@config-') { + $property = substr($name, 8); + $default = isset($field[$property]) ? $field[$property] : null; + $config = self::getGrav()['config']->get($value, $default); + + if (!is_null($config)) { + $field[$property] = $config; + } + } + } + } /** * Add property to the definition. From fb3e68e16e124dda0cf211d3aa78146af377d92b Mon Sep 17 00:00:00 2001 From: Maciej Ka Date: Tue, 24 Nov 2015 23:34:47 +0000 Subject: [PATCH 48/79] removed old parameters --- system/src/Grav/Common/Data/Blueprint.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/system/src/Grav/Common/Data/Blueprint.php b/system/src/Grav/Common/Data/Blueprint.php index aefd1b75c..04bf51040 100644 --- a/system/src/Grav/Common/Data/Blueprint.php +++ b/system/src/Grav/Common/Data/Blueprint.php @@ -346,7 +346,7 @@ protected function parseFormFields(array &$fields, $params, $prefix, array &$cur $this->addProperty($prefix . $key); foreach($field['fields'] as $subName => &$subField) { - $this->parseFormField($subField, $prefix, $key, false); + $this->parseFormField($subField); } if (isset($field['validate']['rule']) && $field['type'] !== 'ignore') { @@ -356,7 +356,7 @@ protected function parseFormFields(array &$fields, $params, $prefix, array &$cur $this->rules[$prefix . $key] = &$field; $this->addProperty($prefix . $key); - $this->parseFormField($field, $prefix, $key, true); + $this->parseFormField($field); if (isset($field['validate']['rule']) && $field['type'] !== 'ignore') { $field['validate'] += $this->getRule($field['validate']['rule']); From edfd7db88b7db461d32850d40780046ba1abf91e Mon Sep 17 00:00:00 2001 From: Maciej Ka Date: Tue, 24 Nov 2015 23:48:22 +0000 Subject: [PATCH 49/79] pretty & clean --- system/src/Grav/Common/Data/Blueprint.php | 40 +++++++++-------------- 1 file changed, 15 insertions(+), 25 deletions(-) diff --git a/system/src/Grav/Common/Data/Blueprint.php b/system/src/Grav/Common/Data/Blueprint.php index 04bf51040..8598d8436 100644 --- a/system/src/Grav/Common/Data/Blueprint.php +++ b/system/src/Grav/Common/Data/Blueprint.php @@ -335,33 +335,23 @@ protected function parseFormFields(array &$fields, $params, $prefix, array &$cur // Recursively get all the nested fields. $newParams = array_intersect_key($this->filter, $field); $this->parseFormFields($field['fields'], $newParams, $prefix, $current[$key]['fields']); - } else if( $field['type'] === 'list') { - // Lists have different structure for fields (one level deeper) - // This means we need to loop through the list to get to the actual field - // The property and rule setting need to be set on the list field - // and NOT on the children, which is why we need to duplicate some of - // the code below :( - - $this->rules[$prefix . $key] = &$field; - $this->addProperty($prefix . $key); - - foreach($field['fields'] as $subName => &$subField) { - $this->parseFormField($subField); - } - - if (isset($field['validate']['rule']) && $field['type'] !== 'ignore') { - $field['validate'] += $this->getRule($field['validate']['rule']); - } } else if ($field['type'] !== 'ignore') { $this->rules[$prefix . $key] = &$field; - $this->addProperty($prefix . $key); - - $this->parseFormField($field); - - if (isset($field['validate']['rule']) && $field['type'] !== 'ignore') { - $field['validate'] += $this->getRule($field['validate']['rule']); - } - } + $this->addProperty($prefix . $key); + + if ($field['type'] === 'list') { + // we loop through list to get the actual field + foreach($field['fields'] as $subName => &$subField) { + $this->parseFormField($subField); + } + } else { + $this->parseFormField($field); + } + + if (isset($field['validate']['rule']) && $field['type'] !== 'ignore') { + $field['validate'] += $this->getRule($field['validate']['rule']); + } + } } } /** From 8d8420c0d66395b47337e6ee54bb46b498660969 Mon Sep 17 00:00:00 2001 From: Flavio Copes Date: Wed, 25 Nov 2015 22:30:45 +0100 Subject: [PATCH 50/79] If the page does not exist trigger a 404 --- system/src/Grav/Common/Twig/Twig.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/system/src/Grav/Common/Twig/Twig.php b/system/src/Grav/Common/Twig/Twig.php index 0f5f4b97d..f88f835b6 100644 --- a/system/src/Grav/Common/Twig/Twig.php +++ b/system/src/Grav/Common/Twig/Twig.php @@ -297,6 +297,11 @@ public function processSite($format = null) $this->grav->fireEvent('onTwigSiteVariables'); $pages = $this->grav['pages']; $page = $this->grav['page']; + + if (!$page) { + throw new \RuntimeException('Page Not Found', 404); + } + $content = $page->content(); $config = $this->grav['config']; From e8972a6aa51903e31ea346e7a926e3dffc63de22 Mon Sep 17 00:00:00 2001 From: Andy Miller Date: Wed, 25 Nov 2015 15:41:53 -0700 Subject: [PATCH 51/79] vendor updates --- composer.lock | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/composer.lock b/composer.lock index 25c0429a7..d19e9a3f8 100644 --- a/composer.lock +++ b/composer.lock @@ -1,11 +1,10 @@ { "_readme": [ "This file locks the dependencies of your project to a known state", - "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", + "Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], "hash": "b1323e540382de7390663756b3a87de7", - "content-hash": "158cd4e3e1a40d5d1db9e3f29c24d83a", "packages": [ { "name": "doctrine/cache", @@ -217,12 +216,12 @@ "source": { "type": "git", "url": "https://github.com/filp/whoops.git", - "reference": "50a288b51058fa94cf5b37cfa4277535983cc9d5" + "reference": "9a393ceb80f7497b6513feb574638e87048fed55" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/filp/whoops/zipball/50a288b51058fa94cf5b37cfa4277535983cc9d5", - "reference": "50a288b51058fa94cf5b37cfa4277535983cc9d5", + "url": "https://api.github.com/repos/filp/whoops/zipball/9a393ceb80f7497b6513feb574638e87048fed55", + "reference": "9a393ceb80f7497b6513feb574638e87048fed55", "shasum": "" }, "require": { @@ -267,7 +266,7 @@ "whoops", "zf2" ], - "time": "2015-11-14 20:08:27" + "time": "2015-09-27 09:47:06" }, { "name": "gregwar/cache", From b259927348b50400beab51465eb07a65424d2028 Mon Sep 17 00:00:00 2001 From: Andy Miller Date: Wed, 25 Nov 2015 15:42:11 -0700 Subject: [PATCH 52/79] no message --- user/localhost/config/security.yaml | 1 + 1 file changed, 1 insertion(+) create mode 100644 user/localhost/config/security.yaml diff --git a/user/localhost/config/security.yaml b/user/localhost/config/security.yaml new file mode 100644 index 000000000..715f1c778 --- /dev/null +++ b/user/localhost/config/security.yaml @@ -0,0 +1 @@ +salt: Y5UKcWPzyQ7XT0 From 77db54c50dbf379eaae5d41eff98706e127cbeba Mon Sep 17 00:00:00 2001 From: Flavio Copes Date: Wed, 25 Nov 2015 22:50:29 +0100 Subject: [PATCH 53/79] Revert "If the page does not exist trigger a 404" This reverts commit 8d8420c0d66395b47337e6ee54bb46b498660969. --- system/src/Grav/Common/Twig/Twig.php | 5 ----- 1 file changed, 5 deletions(-) diff --git a/system/src/Grav/Common/Twig/Twig.php b/system/src/Grav/Common/Twig/Twig.php index f88f835b6..0f5f4b97d 100644 --- a/system/src/Grav/Common/Twig/Twig.php +++ b/system/src/Grav/Common/Twig/Twig.php @@ -297,11 +297,6 @@ public function processSite($format = null) $this->grav->fireEvent('onTwigSiteVariables'); $pages = $this->grav['pages']; $page = $this->grav['page']; - - if (!$page) { - throw new \RuntimeException('Page Not Found', 404); - } - $content = $page->content(); $config = $this->grav['config']; From 793ac1a1bbabf71f7834f57316a54de19e0325c9 Mon Sep 17 00:00:00 2001 From: Flavio Copes Date: Thu, 26 Nov 2015 11:54:05 +0100 Subject: [PATCH 54/79] Revert "no message" This reverts commit b259927348b50400beab51465eb07a65424d2028. --- user/localhost/config/security.yaml | 1 - 1 file changed, 1 deletion(-) delete mode 100644 user/localhost/config/security.yaml diff --git a/user/localhost/config/security.yaml b/user/localhost/config/security.yaml deleted file mode 100644 index 715f1c778..000000000 --- a/user/localhost/config/security.yaml +++ /dev/null @@ -1 +0,0 @@ -salt: Y5UKcWPzyQ7XT0 From 63890661fea8d15fd0917bda45c93ed063fe8dfa Mon Sep 17 00:00:00 2001 From: Flavio Copes Date: Thu, 26 Nov 2015 11:55:13 +0100 Subject: [PATCH 55/79] Add security.yaml to the ignored files --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 01b701cf8..755814004 100644 --- a/.gitignore +++ b/.gitignore @@ -22,6 +22,7 @@ user/plugins/* !user/plugins/.* user/themes/* !user/themes/.* +user/localhost/config/security.yaml # OS Generated .DS_Store* From ae39aabee1eaf6f2f67d5fb7421e1b538834b7c9 Mon Sep 17 00:00:00 2001 From: Flavio Copes Date: Thu, 26 Nov 2015 16:02:06 +0100 Subject: [PATCH 56/79] Include set_time_limit in a try/catch --- system/src/Grav/Common/GPM/Response.php | 5 ++--- system/src/Grav/Common/Utils.php | 5 ++--- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/system/src/Grav/Common/GPM/Response.php b/system/src/Grav/Common/GPM/Response.php index f933f37f0..08b0baeb1 100644 --- a/system/src/Grav/Common/GPM/Response.php +++ b/system/src/Grav/Common/GPM/Response.php @@ -78,10 +78,9 @@ public static function get($uri = '', $options = [], $callback = null) throw new \RuntimeException('Could not start an HTTP request. `allow_url_open` is disabled and `cURL` is not available'); } - // disable time limit if possible to help with slow downloads - if (!Utils::isFunctionDisabled('set_time_limit') && !ini_get('safe_mode')) { + try { set_time_limit(0); - } + } catch (\Exception $e) {} $options = array_replace_recursive(self::$defaults, $options); $method = 'get' . ucfirst(strtolower(self::$method)); diff --git a/system/src/Grav/Common/Utils.php b/system/src/Grav/Common/Utils.php index 90e79ffa2..ead9e627d 100644 --- a/system/src/Grav/Common/Utils.php +++ b/system/src/Grav/Common/Utils.php @@ -218,10 +218,9 @@ public static function download($file, $force_download = true) $file_parts = pathinfo($file); $filesize = filesize($file); - // check if this function is available, if so use it to stop any timeouts - if (function_exists('set_time_limit')) { + try { set_time_limit(0); - } + } catch (\Exception $e) {} ignore_user_abort(false); From 43783f3ce6a94c1941f987e21a7433e595e43380 Mon Sep 17 00:00:00 2001 From: Flavio Copes Date: Thu, 26 Nov 2015 16:18:05 +0100 Subject: [PATCH 57/79] Correct the set_time_limit if --- system/src/Grav/Common/GPM/Response.php | 5 ++++- system/src/Grav/Common/Utils.php | 24 +++++++++++++++++++++++- 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/system/src/Grav/Common/GPM/Response.php b/system/src/Grav/Common/GPM/Response.php index 08b0baeb1..fcaed82b3 100644 --- a/system/src/Grav/Common/GPM/Response.php +++ b/system/src/Grav/Common/GPM/Response.php @@ -78,8 +78,11 @@ public static function get($uri = '', $options = [], $callback = null) throw new \RuntimeException('Could not start an HTTP request. `allow_url_open` is disabled and `cURL` is not available'); } + // check if this function is available, if so use it to stop any timeouts try { - set_time_limit(0); + if (!Utils::isFunctionDisabled('set_time_limit') && !ini_get('safe_mode') && function_exists('set_time_limit')) { + set_time_limit(0); + } } catch (\Exception $e) {} $options = array_replace_recursive(self::$defaults, $options); diff --git a/system/src/Grav/Common/Utils.php b/system/src/Grav/Common/Utils.php index ead9e627d..815959927 100644 --- a/system/src/Grav/Common/Utils.php +++ b/system/src/Grav/Common/Utils.php @@ -218,8 +218,11 @@ public static function download($file, $force_download = true) $file_parts = pathinfo($file); $filesize = filesize($file); + // check if this function is available, if so use it to stop any timeouts try { - set_time_limit(0); + if (!Utils::isFunctionDisabled('set_time_limit') && !ini_get('safe_mode') && function_exists('set_time_limit')) { + set_time_limit(0); + } } catch (\Exception $e) {} ignore_user_abort(false); @@ -410,6 +413,25 @@ public static function date2timestamp($date) } } + /** + * Get value of an array using dot notation + */ + public static function resolve(array $array, $path, $default = null) + { + $current = $array; + $p = strtok($path, '.'); + + while ($p !== false) { + if (!isset($current[$p])) { + return $default; + } + $current = $current[$p]; + $p = strtok('.'); + } + + return $current; + } + /** * Checks if a value is positive * From 5b254f4cf8ed47443711be9f633b114db599153a Mon Sep 17 00:00:00 2001 From: Flavio Copes Date: Thu, 26 Nov 2015 16:19:29 +0100 Subject: [PATCH 58/79] Drop accidentally added method --- system/src/Grav/Common/Utils.php | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/system/src/Grav/Common/Utils.php b/system/src/Grav/Common/Utils.php index 815959927..619b5d899 100644 --- a/system/src/Grav/Common/Utils.php +++ b/system/src/Grav/Common/Utils.php @@ -413,25 +413,6 @@ public static function date2timestamp($date) } } - /** - * Get value of an array using dot notation - */ - public static function resolve(array $array, $path, $default = null) - { - $current = $array; - $p = strtok($path, '.'); - - while ($p !== false) { - if (!isset($current[$p])) { - return $default; - } - $current = $current[$p]; - $p = strtok('.'); - } - - return $current; - } - /** * Checks if a value is positive * From 500c548af49eb60e15fb97d01b4c62a7c919be5f Mon Sep 17 00:00:00 2001 From: Kruno H Date: Thu, 26 Nov 2015 22:48:02 +0100 Subject: [PATCH 59/79] Update hr.yaml added PLURAL_MORE_THAN_TWO translations --- system/languages/hr.yaml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/system/languages/hr.yaml b/system/languages/hr.yaml index 0d84281c5..3dbd80218 100644 --- a/system/languages/hr.yaml +++ b/system/languages/hr.yaml @@ -26,12 +26,18 @@ NICETIME: YR: g DEC: des SECOND_PLURAL: sekundi + SECOND_PLURAL_MORE_THAN_TWO: sekunde MINUTE_PLURAL: minuta + MINUTE_PLURAL_MORE_THAN_TWO: minute HOUR_PLURAL: sati + HOUR_PLURAL_MORE_THAN_TWO: sata DAY_PLURAL: dana WEEK_PLURAL: tjedana + WEEK_PLURAL_MORE_THAN_TWO: tjedna MONTH_PLURAL: mjeseci + MONTH_PLURAL_MORE_THAN_TWO: mjeseca YEAR_PLURAL: godina + YEAR_PLURAL_MORE_THAN_TWO: godine DECADE_PLURAL: desetljeća SEC_PLURAL: sek MIN_PLURAL: min From 2b1a102efa63f0dd128e7e488a2d3536e9fb4b05 Mon Sep 17 00:00:00 2001 From: nazwa Date: Thu, 26 Nov 2015 22:18:27 +0000 Subject: [PATCH 60/79] Web.config update web.config now correctly allows for static files inside vendor folder, just like .htaccess --- web.config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web.config b/web.config index c3dd31424..e09e94fb5 100644 --- a/web.config +++ b/web.config @@ -46,7 +46,7 @@ - + From b399d8e3b970ae047f94a103ddfc193b03d1d9d4 Mon Sep 17 00:00:00 2001 From: Flavio Copes Date: Fri, 27 Nov 2015 19:27:58 +0100 Subject: [PATCH 61/79] Drop check used by the array field, introduced by https://github.com/getgrav/grav/commit/57b18edb559ea4dbaff3b89177492bf99ad2a4a3 --- system/src/Grav/Common/Data/Blueprint.php | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/system/src/Grav/Common/Data/Blueprint.php b/system/src/Grav/Common/Data/Blueprint.php index 8598d8436..3e1dc1f09 100644 --- a/system/src/Grav/Common/Data/Blueprint.php +++ b/system/src/Grav/Common/Data/Blueprint.php @@ -242,9 +242,6 @@ protected function filterArray(array $data, array $rules) if ($rule) { // Item has been defined in blueprints. - if (is_array($field) && count($field) == 1 && reset($field) == '') { - continue; - } $field = Validation::filter($field, $rule); } elseif (is_array($field) && is_array($val)) { // Array has been defined in blueprints. @@ -338,7 +335,7 @@ protected function parseFormFields(array &$fields, $params, $prefix, array &$cur } else if ($field['type'] !== 'ignore') { $this->rules[$prefix . $key] = &$field; $this->addProperty($prefix . $key); - + if ($field['type'] === 'list') { // we loop through list to get the actual field foreach($field['fields'] as $subName => &$subField) { @@ -347,7 +344,7 @@ protected function parseFormFields(array &$fields, $params, $prefix, array &$cur } else { $this->parseFormField($field); } - + if (isset($field['validate']['rule']) && $field['type'] !== 'ignore') { $field['validate'] += $this->getRule($field['validate']['rule']); } From 1465c26b1fa203f4d19b26a3fb2771288c50b89a Mon Sep 17 00:00:00 2001 From: AQNOUCH Mohammed Date: Sat, 28 Nov 2015 19:20:59 +0000 Subject: [PATCH 62/79] =?UTF-8?q?Change=20the=20first=20letter=20of=20the?= =?UTF-8?q?=20word=20'fran=C3=A7ais'=20to=20uppercase?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- system/src/Grav/Common/Language/LanguageCodes.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system/src/Grav/Common/Language/LanguageCodes.php b/system/src/Grav/Common/Language/LanguageCodes.php index 2683c8b0a..86b130a90 100644 --- a/system/src/Grav/Common/Language/LanguageCodes.php +++ b/system/src/Grav/Common/Language/LanguageCodes.php @@ -193,7 +193,7 @@ class LanguageCodes ], "fr" => [ "name" => "French", - "nativeName" => "français" + "nativeName" => "Français" ], "ff" => [ "name" => "Fula", From 3a7abeb18b11ccb7e672cd098fd12e75d43198e2 Mon Sep 17 00:00:00 2001 From: Andy Miller Date: Sun, 29 Nov 2015 20:00:16 -0700 Subject: [PATCH 63/79] Added support to set classes and id on a medium object --- system/src/Grav/Common/Page/Medium/Medium.php | 35 ++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/system/src/Grav/Common/Page/Medium/Medium.php b/system/src/Grav/Common/Page/Medium/Medium.php index b36864c27..a5e4001a7 100644 --- a/system/src/Grav/Common/Page/Medium/Medium.php +++ b/system/src/Grav/Common/Page/Medium/Medium.php @@ -214,8 +214,9 @@ public function parsedownElement($title = null, $alt = null, $class = null, $res else $style .= $key . ': ' . $value . ';'; } - if ($style) + if ($style) { $attributes['style'] = $style; + } if (empty($attributes['title'])) { if (!empty($title)) { @@ -392,6 +393,38 @@ public function lightbox($width = null, $height = null, $reset = true) return $this->link($reset, $attributes); } + /** + * Add a class to the element from Markdown or Twig + * Example: ![Example](myimg.png?classes=float-left) or ![Example](myimg.png?classes=myclass1,myclass2) + * + * @return $this + */ + public function classes() + { + $classes = func_get_args(); + if (!empty($classes)) { + $this->attributes['class'] = implode(',', (array)$classes); + } + + return $this; + } + + /** + * Add an id to the element from Markdown or Twig + * Example: ![Example](myimg.png?id=primary-img) + * + * @param $id + * @return $this + */ + public function id($id) + { + if (is_string($id)) { + $this->attributes['id'] = trim($id); + } + + return $this; + } + /** * Allows to add an inline style attribute from Markdown or Twig * Example: ![Example](myimg.png?style=float:left) From 00d8717d7c3b7d19078af1fd40855086c39ba7e9 Mon Sep 17 00:00:00 2001 From: Hugo Avila Date: Sun, 29 Nov 2015 21:26:21 -0800 Subject: [PATCH 64/79] Create es.yaml --- system/languages/es.yaml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 system/languages/es.yaml diff --git a/system/languages/es.yaml b/system/languages/es.yaml new file mode 100644 index 000000000..fdb58a261 --- /dev/null +++ b/system/languages/es.yaml @@ -0,0 +1,4 @@ +FORM: + VALIDATION_FAIL: Falló la validación. + INVALID_INPUT: "Dato inválido en: " + MISSING_REQUIRED_FIELD: "Falta el campo requerido: " From c580399db6ed3744426f956bea01ff291b34d1ad Mon Sep 17 00:00:00 2001 From: Hugo Avila Date: Sun, 29 Nov 2015 21:33:08 -0800 Subject: [PATCH 65/79] Update es.yaml --- system/languages/es.yaml | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/system/languages/es.yaml b/system/languages/es.yaml index fdb58a261..41df5d55b 100644 --- a/system/languages/es.yaml +++ b/system/languages/es.yaml @@ -1,4 +1,42 @@ +NICETIME: + NO_DATE_PROVIDED: No se proporcionó fecha + BAD_DATE: Fecha erronea + AGO: antes + FROM_NOW: desde ahora + SECOND: segundo + MINUTE: minuto + HOUR: hora + DAY: dia + WEEK: semana + MONTH: mes + YEAR: año + DECADE: decada + SEC: seg + MIN: min + HR: hr + DAY: dia + WK: sem + MO: mes + YR: yr + DEC: dec + SECOND_PLURAL: segundos + MINUTE_PLURAL: minutos + HOUR_PLURAL: horas + DAY_PLURAL: días + WEEK_PLURAL: semanas + MONTH_PLURAL: meses + YEAR_PLURAL: años + DECADE_PLURAL: decadas + SEC_PLURAL: segs + MIN_PLURAL: mins + HR_PLURAL: hrs + DAY_PLURAL: dias + WK_PLURAL: sem + MO_PLURAL: mes + YR_PLURAL: años + DEC_PLURAL: decs FORM: VALIDATION_FAIL: Falló la validación. INVALID_INPUT: "Dato inválido en: " MISSING_REQUIRED_FIELD: "Falta el campo requerido: " + From b33ab43ff9a3badbb31dccba4dad002cf6f8f211 Mon Sep 17 00:00:00 2001 From: Hugo Avila Date: Sun, 29 Nov 2015 21:39:51 -0800 Subject: [PATCH 66/79] Update en.yaml --- system/languages/en.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/system/languages/en.yaml b/system/languages/en.yaml index a1eab6f19..654c0d3e5 100644 --- a/system/languages/en.yaml +++ b/system/languages/en.yaml @@ -95,3 +95,4 @@ NICETIME: FORM: VALIDATION_FAIL: Validation failed: INVALID_INPUT: Invalid input in + MISSING_REQUIRED_FIELD: Missing required field: From f3d0e1037895ac2b4cabc6ff134b2c651810209d Mon Sep 17 00:00:00 2001 From: Hugo Avila Date: Sun, 29 Nov 2015 21:43:05 -0800 Subject: [PATCH 67/79] Update Blueprint.php --- system/src/Grav/Common/Data/Blueprint.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/system/src/Grav/Common/Data/Blueprint.php b/system/src/Grav/Common/Data/Blueprint.php index 3e1dc1f09..8be6a8ea8 100644 --- a/system/src/Grav/Common/Data/Blueprint.php +++ b/system/src/Grav/Common/Data/Blueprint.php @@ -465,7 +465,9 @@ protected function checkRequired(array $data, array $fields) && $field['validate']['required'] === true && empty($data[$name])) { $value = isset($field['label']) ? $field['label'] : $field['name']; - throw new \RuntimeException("Missing required field: {$value}"); + $language = self::getGrav()['language']; + $message = sprintf($language->translate('FORM.MISSING_REQUIRED_FIELD', null, true) . ' %s', $value); + throw new \RuntimeException($message); } } } From af5c52c52f58e802115a2a1c51f3e0d8d021aa99 Mon Sep 17 00:00:00 2001 From: Flavio Copes Date: Mon, 30 Nov 2015 14:36:23 +0100 Subject: [PATCH 68/79] Update session name hints --- system/config/system.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system/config/system.yaml b/system/config/system.yaml index 8f09cf599..f64620cbf 100644 --- a/system/config/system.yaml +++ b/system/config/system.yaml @@ -108,6 +108,6 @@ media: session: enabled: true # Enable Session support timeout: 1800 # Timeout in seconds - name: grav-site # Name prefix of the session cookie + name: grav-site # Name prefix of the session cookie. Use alphanumeric, dashes or underscores only. Do not use dots in the session name From e4849975156750da26930344aa220d894094d6df Mon Sep 17 00:00:00 2001 From: Matias Griese Date: Mon, 30 Nov 2015 18:56:34 +0200 Subject: [PATCH 69/79] If environment does not have its own directory, remove it from the lookup --- system/src/Grav/Common/Config/Setup.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/system/src/Grav/Common/Config/Setup.php b/system/src/Grav/Common/Config/Setup.php index 9bb66853c..70d556472 100644 --- a/system/src/Grav/Common/Config/Setup.php +++ b/system/src/Grav/Common/Config/Setup.php @@ -232,6 +232,12 @@ protected function check(UniformResourceLocator $locator) ); } + if (!$locator->findResource('environment://', true)) { + // If environment does not have its own directory, remove it from the lookup. + $this->set('streams.schemes.environment.prefixes', ['' => []]); + $this->initializeLocator($locator); + } + // Create security.yaml if it doesn't exist. $filename = $locator->findResource('config://security.yaml', true, true); $file = YamlFile::instance($filename); From 20079754288cd6d6cc9d7e53c753a60fb09ba27c Mon Sep 17 00:00:00 2001 From: Matias Griese Date: Mon, 30 Nov 2015 18:56:34 +0200 Subject: [PATCH 70/79] If environment does not have its own directory, remove it from the lookup --- system/src/Grav/Common/Config/Setup.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/system/src/Grav/Common/Config/Setup.php b/system/src/Grav/Common/Config/Setup.php index 9bb66853c..70d556472 100644 --- a/system/src/Grav/Common/Config/Setup.php +++ b/system/src/Grav/Common/Config/Setup.php @@ -232,6 +232,12 @@ protected function check(UniformResourceLocator $locator) ); } + if (!$locator->findResource('environment://', true)) { + // If environment does not have its own directory, remove it from the lookup. + $this->set('streams.schemes.environment.prefixes', ['' => []]); + $this->initializeLocator($locator); + } + // Create security.yaml if it doesn't exist. $filename = $locator->findResource('config://security.yaml', true, true); $file = YamlFile::instance($filename); From 5b0f905ae36e7c4e6e9ba274cde5d3a2095d8b25 Mon Sep 17 00:00:00 2001 From: Flavio Copes Date: Mon, 30 Nov 2015 19:20:20 +0100 Subject: [PATCH 71/79] Update italian lang file --- system/languages/it.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/system/languages/it.yaml b/system/languages/it.yaml index 5f61ad3c1..c6a92ec9d 100644 --- a/system/languages/it.yaml +++ b/system/languages/it.yaml @@ -19,3 +19,7 @@ NICETIME: MONTH_PLURAL: mesi YEAR_PLURAL: anni DECADE_PLURAL: decadi +FORM: + VALIDATION_FAIL: Validazione fallita: + INVALID_INPUT: Input invalido in + MISSING_REQUIRED_FIELD: Campo richiesto mancante: From 84ad15253698fba5a104bd335e78fcc69e0f1116 Mon Sep 17 00:00:00 2001 From: Matias Griese Date: Mon, 30 Nov 2015 20:44:08 +0200 Subject: [PATCH 72/79] Only create environmental config if the directory exists --- system/src/Grav/Common/Config/Setup.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/system/src/Grav/Common/Config/Setup.php b/system/src/Grav/Common/Config/Setup.php index 70d556472..90178d6b2 100644 --- a/system/src/Grav/Common/Config/Setup.php +++ b/system/src/Grav/Common/Config/Setup.php @@ -232,9 +232,9 @@ protected function check(UniformResourceLocator $locator) ); } - if (!$locator->findResource('environment://', true)) { + if (!$locator->findResource('environment://config', true)) { // If environment does not have its own directory, remove it from the lookup. - $this->set('streams.schemes.environment.prefixes', ['' => []]); + $this->set('streams.schemes.environment.prefixes', ['config' => []]); $this->initializeLocator($locator); } From 199c0a08eafa7c4c66c57a1e4e9a54928581b461 Mon Sep 17 00:00:00 2001 From: Andy Miller Date: Mon, 30 Nov 2015 12:18:32 -0700 Subject: [PATCH 73/79] Should not be able to set parent to self (infinite loop) #308 --- system/src/Grav/Common/Page/Pages.php | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/system/src/Grav/Common/Page/Pages.php b/system/src/Grav/Common/Page/Pages.php index 53790bac6..c579afac9 100644 --- a/system/src/Grav/Common/Page/Pages.php +++ b/system/src/Grav/Common/Page/Pages.php @@ -498,7 +498,22 @@ public static function parents() /** @var Pages $pages */ $pages = $grav['pages']; - return $pages->getList(); + $parents = $pages->getList(); + + /** @var Admin $admin */ + $admin = $grav['admin']; + + // Remove current route from parents + if (isset($admin)) { + $page = $admin->getPage($admin->route); + $page_route = $page->route(); + if (isset($parents[$page_route])) { + unset($parents[$page_route]); + } + + } + + return $parents; } /** From 1170f2f58d8b634dce251c28fc49784d019ff958 Mon Sep 17 00:00:00 2001 From: Marc-Antoine Thevenet Date: Mon, 30 Nov 2015 15:30:22 -0400 Subject: [PATCH 74/79] Update fr.yaml Update following the /en file Thanks --- system/languages/fr.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/system/languages/fr.yaml b/system/languages/fr.yaml index dbed71270..4dcafc8a8 100644 --- a/system/languages/fr.yaml +++ b/system/languages/fr.yaml @@ -95,3 +95,4 @@ NICETIME: FORM: VALIDATION_FAIL: La validation a échoué : INVALID_INPUT: Saisie non valide + MISSING_REQUIRED_FIELD: Champ obligatoire manquant : From a67c1780c11cc67e4c773ae0e8d5a4b9f7dd25f7 Mon Sep 17 00:00:00 2001 From: Andy Miller Date: Mon, 30 Nov 2015 19:16:46 -0700 Subject: [PATCH 75/79] Added a new `@self.all` case that gets all children modular and non-modular --- system/src/Grav/Common/Page/Page.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/system/src/Grav/Common/Page/Page.php b/system/src/Grav/Common/Page/Page.php index 00a0f756f..50b3fa83f 100644 --- a/system/src/Grav/Common/Page/Page.php +++ b/system/src/Grav/Common/Page/Page.php @@ -2002,16 +2002,16 @@ protected function evaluate($value) case 'children': $results = $this->children()->nonModular(); break; - + case 'all': + $results = $this->children(); + 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; From b1e16b2206902c21c1170d949cd3a0d8e6dfc287 Mon Sep 17 00:00:00 2001 From: Djamil Legato Date: Mon, 30 Nov 2015 18:22:30 -0800 Subject: [PATCH 76/79] Added validator for `file` input fields --- system/src/Grav/Common/Data/Validation.php | 28 ++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/system/src/Grav/Common/Data/Validation.php b/system/src/Grav/Common/Data/Validation.php index c232467ba..9cebd0d76 100644 --- a/system/src/Grav/Common/Data/Validation.php +++ b/system/src/Grav/Common/Data/Validation.php @@ -31,6 +31,11 @@ public static function validate($value, array $field) return; } + // 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']))) { + return; + } + // Get language class $language = self::getGrav()['language']; @@ -78,6 +83,11 @@ public static function filter($value, array $field) return null; } + // 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']))) { + return null; + } + // if this is a YAML field, simply parse it and return the value if (isset($field['yaml']) && $field['yaml'] === true) { try { @@ -258,6 +268,24 @@ public static function typeToggle($value, array $params, array $field) return self::typeArray((array) $value, $params, $field); } + /** + * Custom input: file + * + * @param mixed $value Value to be validated. + * @param array $params Validation parameters. + * @param array $field Blueprint for the field. + * @return bool True if validation succeeded. + */ + public static function typeFile($value, array $params, array $field) + { + return self::typeArray((array) $value, $params, $field); + } + + protected static function filterFile($value, array $params, array $field) + { + return (array) $value; + } + /** * HTML5 input: select * From 90edf95077ce9c8fff7b76433babee9aa6be0e33 Mon Sep 17 00:00:00 2001 From: Andy Miller Date: Tue, 1 Dec 2015 11:15:00 -0700 Subject: [PATCH 77/79] renamed LICENSE to LICENSE.txt --- .htaccess | 2 +- LICENSE => LICENSE.txt | 0 htaccess.txt | 2 +- lighttpd.conf | 2 +- nginx.conf | 2 +- 5 files changed, 4 insertions(+), 4 deletions(-) rename LICENSE => LICENSE.txt (100%) diff --git a/.htaccess b/.htaccess index 55bcd08a7..4017987a9 100644 --- a/.htaccess +++ b/.htaccess @@ -54,7 +54,7 @@ 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|htaccess.txt|\.htaccess)$ error [F] +RewriteRule ^(LICENSE.txt|composer.lock|composer.json|nginx.conf|web.config|htaccess.txt|\.htaccess)$ error [F] ## End - Security diff --git a/LICENSE b/LICENSE.txt similarity index 100% rename from LICENSE rename to LICENSE.txt diff --git a/htaccess.txt b/htaccess.txt index 55bcd08a7..4017987a9 100644 --- a/htaccess.txt +++ b/htaccess.txt @@ -54,7 +54,7 @@ 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|htaccess.txt|\.htaccess)$ error [F] +RewriteRule ^(LICENSE.txt|composer.lock|composer.json|nginx.conf|web.config|htaccess.txt|\.htaccess)$ error [F] ## End - Security diff --git a/lighttpd.conf b/lighttpd.conf index 69acbe484..8429b865f 100644 --- a/lighttpd.conf +++ b/lighttpd.conf @@ -27,7 +27,7 @@ url.rewrite-if-not-file = ( ) #IMPROVING SECURITY -$HTTP["url"] =~ "^/grav_path/(LICENSE|composer.json|composer.lock|nginx.conf|web.config)$" { +$HTTP["url"] =~ "^/grav_path/(LICENSE.txt|composer.json|composer.lock|nginx.conf|web.config)$" { url.access-deny = ("") } $HTTP["url"] =~ "^/grav_path/(.git|cache|bin|logs|backup)/(.*)" { diff --git a/nginx.conf b/nginx.conf index d325ff308..dbc581fee 100644 --- a/nginx.conf +++ b/nginx.conf @@ -38,7 +38,7 @@ server { # 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; } + location ~ /(LICENSE.txt|composer.lock|composer.json|nginx.conf|web.config|htaccess.txt|\.htaccess) { return 403; } ## End - Security } From 0af4fb351cae83321a5aae7bf125cab56ed5cc23 Mon Sep 17 00:00:00 2001 From: Andy Miller Date: Tue, 1 Dec 2015 12:47:10 -0700 Subject: [PATCH 78/79] changelog updated --- CHANGELOG.md | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ddb6ba57d..74be9c451 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,13 +2,26 @@ ## XX/XX/2015 1. [](#new) - * Refactor Data classes to use NestedArrayAccess instead of DataMutatorTrait + * Refactor Config classes for improved performance! + * Refactor Data classes to use `NestedArrayAccess` instead of `DataMutatorTrait` + * Added support for `classes` and `id` on medium objects to set CSS values * Data objects: Allow function call chaining * Data objects: Lazy load blueprints only if needed - * Refactor Config classes * Automatically create unique security salt for each configuration -2. [](#bugfix) + * Added Hungarian translation + * Added support for User groups +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 + * Nonces now use random string with random salt to improve performance + * Improved list form handling #475 + * Vendor library updates +1. [](#bugfix) * Fixed help output for `bin/plugin` + * Fix for nested logic for lists and form parsing #273 + * Fix for array form fields and last entry not getting deleted + * Should not be able to set parent to self #308 # v1.0.0-rc.5 ## 11/20/2015 From b605753a6dcf51711d0215eafcd95ce9d1ba5b0c Mon Sep 17 00:00:00 2001 From: Andy Miller Date: Tue, 1 Dec 2015 13:12:59 -0700 Subject: [PATCH 79/79] version update --- CHANGELOG.md | 2 +- system/defines.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 74be9c451..2fc9ca4b7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,5 @@ # v1.0.0-rc.6 -## XX/XX/2015 +## 12/01/2015 1. [](#new) * Refactor Config classes for improved performance! diff --git a/system/defines.php b/system/defines.php index f3aacc286..2e0e01442 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.5'); +define('GRAV_VERSION', '1.0.0-rc.6'); define('DS', '/'); // Directories and Paths