From 62d0fd5f18a9b87153510f48a40954e9d8201ebd Mon Sep 17 00:00:00 2001 From: Andy Miller Date: Fri, 17 Aug 2018 11:34:52 -0600 Subject: [PATCH] Squashed commit of the following: MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 33fffa6a5092bd20a923d9cbef98c089875c6f2e Merge: c381bc83 dbd825f0 Author: Andy Miller Date: Fri Aug 17 11:24:56 2018 -0600 Merge tag '1.5.0' into develop Release v1.5.0 commit dbd825f0b66e16d1b04a3dabf6148624a5a0ea53 Merge: fa7e6be9 8ab0078d Author: Andy Miller Date: Fri Aug 17 11:24:55 2018 -0600 Merge branch 'release/1.5.0' commit 8ab0078d5aa0cd5c4a92f46902108b83c4dfcafd Author: Andy Miller Date: Fri Aug 17 11:24:43 2018 -0600 Prepare for release commit c381bc83040e00c9a8ebe91ac3bda5fe0c217197 Author: Andy Miller Date: Thu Aug 16 14:58:19 2018 -0600 PHP 7.2 by default now commit fb20b58369d5e0140a4fa6da06edf8f40412f7bf Author: Andy Miller Date: Wed Aug 15 17:12:20 2018 -0600 changelog update commit 906017e0c1485e753720a8a347bcc531005841d8 Author: Andy Miller Date: Wed Aug 15 17:12:10 2018 -0600 Added system blueprint for strict_mode settings commit 266369ee0432ed1fa2298c01dd16c79c5c9c7d4e Author: Andy Miller Date: Wed Aug 15 16:15:37 2018 -0600 unified 1.5.0 changelog entry for clarity commit 308ac14dbecce1e058128de734107918feffd4b4 Author: Andy Miller Date: Wed Aug 15 16:11:47 2018 -0600 Updated changelog commit 2a9da765128c946ba9959bd69aa105b7eaae2d12 Merge: 8e435508 8d9efe4f Author: Andy Miller Date: Wed Aug 15 16:08:42 2018 -0600 Merge branch 'develop' into 1.5 commit 8e43550841a87463be47e5821cd33481dc4b0afd Author: Andy Miller Date: Wed Aug 15 15:52:31 2018 -0600 Updated changelog commit 75ac0201d8f947180dcc0f5d6d4fa27bb6cd6461 Author: Djamil Legato Date: Wed Aug 15 13:36:05 2018 -0700 Added support for multiple repos lookup (as array) in .grav/config This will allow to keep clones of repositories on different folders and still be able to symlink them. Example of ~/.grav/config: ``` github_repos: - /Users/my_user/Projects/grav/ - /Users/my_user/Projects/personal/ - /Users/my_user/Projects/work/ ``` commit 8d9efe4ff79f0962335511d1436c2490bd71ed60 Author: Andy Miller Date: Tue Aug 14 19:47:16 2018 -0600 Extra semicolon commit 593400743a68033fff10341265bff2c96dd0d103 Author: Andy Miller Date: Tue Aug 14 19:46:52 2018 -0600 Fix for plugin order commit 42ff8eaeb03014e2605b1aa4f9624e7a62547336 Author: Matias Griese Date: Mon Aug 13 22:28:12 2018 +0300 Make ObjectTrait::serialize() overrides easier commit 5c2f9946f842df68800ef43f1477faf1cba02266 Merge: fde75e1e 63161e62 Author: Matias Griese Date: Mon Aug 13 09:36:07 2018 +0300 Merge branch 'develop' of https://github.com/getgrav/grav into 1.5 commit 63161e62a2bed2089d79e4aae4b4d64b312b7acc Author: Jascha Geerds <41366030+gn-jgeerds@users.noreply.github.com> Date: Fri Aug 10 00:05:24 2018 +0200 Fix broken nounce handling (#2121) * Remove deprecated "getNonceOldStyle" function This commit removes the following functions: - getNonceOldStyle - generateNonceStringOldStyle The functions have been replaced in newer versions of grav. It seems to me that they only existed in order to make a upgrade to a newer version of grav painless (i.e. accept both types of nonce tokens). Nowadays, existing old style nonces are expired long time ago so it should be save to delete the deprecated funtions. * Fix caching of nonces in static class variable Currently, the behavior of `getNonce` is broken because it saves the generated nonce in an array and only use the $action as the key. However, the generated nonce does not only depend on the $action, but also on $plusOneTick. * Fix broken "plusOneTick" for nonces It looks to me that there is a bug in the current implemention of verifyNonce. Here is an example: - 2018-08-01 10:00: We respond to a request and generate a nonce. The current tick is at 35489 - 2018-08-01 10:05: We use the previously generated nonce to make another request. We compare the given nounce with a new generated one (based on the same tick). The result is exactly the same and the request succeeds. - 2018-08-01 14:00: We're now one tick ahead. Remember: A day (24 hours) is separated into two ticks (each 12 hours). A request comes in, we compare the given nounce with a newly generated one based on the current tick (now at 35490). They don't match (which is totally okay). If the comparison fails, we then compare the given nounce with a another, newly generated one. This time, we pass "plusOneTick", to the function, which increases the current tick by one. Our tick is now at 35491. We generate a nonce based on that tick and of course, it still does not match the given nonce. Instead of increasing the tick, we should rather decreasing it by one (i.e. use the previous tick). If the first comparison fails, we use the current tick (35490), decrease it by one (35489) and then compare it again. 35489 is the same tick as in the very first request. This bug leads to a maximum life time of 12 hours for a nonce and in worst case only a few seconds (!) I would like to prove the bug with an unit test but I'm too unexperienced in PHP. Furthermore it seems that we need some kind of library which is able to mock builtin functions (like "time"). Maybe would be a good canditate? commit c84983ad5bced37c3083db3f704e668ddb54384a Author: lucaswillering Date: Thu Aug 9 23:32:53 2018 +0200 Add muted and playsinline attributes (#2124) Fixes: #2099 To be able to add videos to sites that behave as GIFs, two attributes are needed for the videos to be properly handled on iOS and in Chrome: muted and playsinline. Muted Chrome only allows videos to autoplay when the contain the muted attribute. Non-muted videos will not autoplay unless the user has interacted with the site. More details here: https://developers.google.com/web/updates/2017/09/autoplay-policy-changes#new-behaviors Playsinline The playsinline attribute allows developers to specify videos on iPhone should play inline and not automatically enter fullscreen mode when playback begins. More details here: https://developers.google.com/web/updates/2017/09/autoplay-policy-changes#new-behaviors commit 3cee53508e42bd89d8472d7c91cd06ea6092937f Author: atyner <40250786+atyner@users.noreply.github.com> Date: Thu Aug 9 17:32:34 2018 -0400 Typo 'Subscxripted' on default Typography page (#2136) commit fde75e1ed506fb5c3c57c775c8cc0c264ff51bed Author: Matias Griese Date: Wed Aug 8 21:09:25 2018 +0300 Composer update (toolbox) commit 16d2f607c8981249c1039c9710692bbc3322104f Author: Matias Griese Date: Tue Aug 7 23:12:38 2018 +0300 Fixed Uri::parseUrl($url) with no path (part 2) commit 816a3ebd9357362264e9c278cba2abd0372aefca Author: Matias Griese Date: Tue Aug 7 22:46:23 2018 +0300 Fixed Uri::parseUrl($url) with no path commit d59fe2fa3c9037af25b4cfd469895a0dd0e3c1aa Author: Matias Griese Date: Tue Aug 7 22:32:29 2018 +0300 Display better exception message if Grav fails to initialize commit ef55e7d21932038c4e2cfbfbe8988eed3c1953e8 Author: Matias Griese Date: Tue Aug 7 22:30:45 2018 +0300 Added option to disable `SimpleCache` key validation commit 424da520cf9933763258fa773948248fb67bd252 Author: Andy Miller Date: Mon Aug 6 15:37:59 2018 -0600 Fix #2134 - inheritance of theme classes that include digits in camelcase commit 08cb311e5ecbfe48f93dc4528f5ba4d242a68af0 Author: Andy Miller Date: Mon Aug 6 15:36:38 2018 -0600 Fix truncator tests commit e1b5875c5b09d9a73d700872c4dde4a3799349d0 Author: Andy Miller Date: Mon Aug 6 15:22:55 2018 -0600 Update vendor libs commit 7d27206fecf5d443f6dc5f618e7817179a61494e Author: Andy Miller Date: Mon Aug 6 14:56:00 2018 -0600 Fixed #2133 - uppercase fallback media urls commit 18d405d798ea403dcf9799e447d42887e91a8fbb Author: Andy Miller Date: Mon Aug 6 13:09:15 2018 -0600 Improved `Utils::url()` to support query strings commit 34fa50fcf0e92b073cedaee842620ccbd23839bc Author: Matias Griese Date: Fri Aug 3 13:19:26 2018 +0300 Added `FormatterInterface::getDefaultFileExtension()` commit ca3cf2ea3c3c448dc28f1acc6cb3b2d3bab17337 Author: Matias Griese Date: Fri Aug 3 13:03:51 2018 +0300 Added `FormatterInterface::getSupportedFileExtensions()` method, deprecated `getFileExtension()` commit 76fb11366b2200a856acad6cf2355c121a4ed447 Author: Matias Griese Date: Thu Aug 2 22:41:54 2018 +0300 Added `Uri::method()` to get current HTTP method (GET/POST etc) commit e4f28088705fad2cdb901967903b22290dd86b2b Author: Andy Miller Date: Tue Jul 31 11:27:49 2018 -0600 prepare for release commit f7496b5341cb7c33c054ba14d7cdee460c16de9f Merge: ead125d5 2f0d600e Author: Andy Miller Date: Tue Jul 31 11:27:08 2018 -0600 Merge branch 'develop' into 1.5 # Conflicts: # CHANGELOG.md # composer.lock # system/defines.php commit 2f0d600e863017234eb894bc954267fce8354fce Merge: c83852f4 fa7e6be9 Author: Andy Miller Date: Tue Jul 31 11:23:36 2018 -0600 Merge tag '1.4.8' into develop Release v1.4.8 commit fa7e6be95a1bd92851b4e3b9c7e6c0d4334fb01e Merge: 24edf15e cea43a2d Author: Andy Miller Date: Tue Jul 31 11:23:35 2018 -0600 Merge branch 'release/1.4.8' commit cea43a2d21986eec5b7eec30bee3a8ba0ed9644d Author: Andy Miller Date: Tue Jul 31 11:23:24 2018 -0600 Prepare for release commit b7387c874153ac835126e849482d2219fe27e3ef Author: Andy Miller Date: Tue Jul 31 11:23:15 2018 -0600 vendor updates commit c83852f4e1a79bd371a435abaa6c00e5df6984e1 Author: Andy Miller Date: Tue Jul 31 11:20:49 2018 -0600 update changelog commit ce271cf389437d33e0e11fb4610be129dcbe078b Merge: 8ee367e5 db03091c Author: Andy Miller Date: Tue Jul 31 11:17:09 2018 -0600 Merge branch 'develop' of github.com:getgrav/grav into develop commit ead125d59914ed7a7093a41b8c4fdca5911d786a Merge: 6b5849b2 db03091c Author: Matias Griese Date: Mon Jul 30 12:10:24 2018 +0300 Merge branch 'develop' of https://github.com/getgrav/grav into 1.5 commit 8ee367e52e59048f6881ec0e496fd7a3a28bd4ba Author: Andy Miller Date: Fri Jul 27 15:05:45 2018 -0600 Don't allow `null` to be set as Page content commit db03091cff1c9f029781d1831b700332fab39563 Author: Jeremy Gonyea Date: Fri Jul 27 16:23:35 2018 -0400 Added nginx config for hosting in a ddev project (#2117) commit 6b5849b207b2b8e37dbebed7390ed12a140f8a0f Author: Matias Griese Date: Fri Jul 20 22:54:22 2018 +0300 Added `MediaTrait::getMediaCache()` to allow custom caching commit ba0a8c4092ca9e7559c38dc9bf3ad0f7e01c0f74 Author: Matias Griese Date: Fri Jul 20 22:25:22 2018 +0300 Added `MediaTrait::clearMediaCache()` to allow cache to be cleared commit c8ab5d34f76c24385828d32e2d02fb96a3df08dc Merge: a754f697 c9367ba4 Author: Matias Griese Date: Thu Jul 19 10:05:26 2018 +0300 Merge branch 'develop' into 1.5 # Conflicts: # CHANGELOG.md # system/defines.php commit c9367ba4f34c3c2187b57b6662eb9e4e1734ab6d Author: Hugh Barnes Date: Thu Jul 19 13:03:25 2018 +1200 Add Grav version to debug bar Messages tab (#2106) commit a754f697d7a45e4ada327f63442d78fce96f3551 Author: Matias Griese Date: Sat Jul 14 13:22:35 2018 +0300 Added twig filters for casting values: `|string`, `|int`, `|bool`, `|float`, `|array` Made `|markdown` filter HTML safe commit dd75ce515ff7fd5ca82569c3008259bb7e904978 Author: Matias Griese Date: Sat Jul 14 12:12:53 2018 +0300 Code style fix on Twig tags commit ea83b46bfb82b888bdc4b661888f41f1a076f52f Author: Andy Miller Date: Fri Jul 13 16:54:32 2018 -0600 Prepare for release commit e7f628233dea2130a9d0969210a7b44dc81bc06e Author: Andy Miller Date: Fri Jul 13 16:47:49 2018 -0600 Update changelog commit 24edf15e167a601f25f3655e723bc174ede09942 Merge: 5435ee60 a5e97ef8 Author: Andy Miller Date: Fri Jul 13 16:38:56 2018 -0600 Merge branch 'release/1.4.7' commit 70e65129d7f075a3fbf315bbe40bd7905ba3d581 Merge: 8a1f0d49 24edf15e Author: Andy Miller Date: Fri Jul 13 16:38:56 2018 -0600 Merge tag '1.4.7' into develop Release v1.4.7 commit a5e97ef846b0b1fb31af7569edfe09a158ffc823 Author: Andy Miller Date: Fri Jul 13 16:38:46 2018 -0600 Prepare for release commit 8a1f0d49327902c2ea3cf5d8befb650c1d002fcd Author: Andy Miller Date: Fri Jul 13 16:38:04 2018 -0600 update changelog commit f29997a5cf44b4fd435079142b8ea4fcb8f4eead Author: Andy Miller Date: Fri Jul 13 16:35:51 2018 -0600 Minor vendor updates commit 4daec6908cbf8e0247cb9ef256bb7ffb84dc0ca9 Merge: 05028d0d 79bff580 Author: Matias Griese Date: Fri Jul 6 09:17:41 2018 +0300 Merge branch 'develop' of https://github.com/getgrav/grav into 1.5 commit 79bff58021d34bbd4a42944442a9898606d36d36 Author: Timothy Cyrus Date: Thu Jul 5 16:14:57 2018 -0400 Change getBasename to getFilename where possible (#2087) * Update Pages.php * Update Themes.php * Update Installer.php * Update Plugins.php * Update ConfigFileFinder.php commit 05028d0d9bb0e108d07e2cf9c357f7628687faab Author: Matias Griese Date: Thu Jul 5 13:14:17 2018 +0300 Changelog update commit b4148804e1f636059718ce6ed8e79ecff7d85a6c Author: Matias Griese Date: Thu Jul 5 13:12:59 2018 +0300 Criteria: Added support for `LENGTH()`, `LOWER()`, `UPPER()`, `LTRIM()`, `RTRIM()` and `TRIM()` commit 07f8dfb1c5cab9e9a78c91580eb7666a972656df Author: Matias Griese Date: Thu Jul 5 12:27:28 2018 +0300 Made `ObjectCollection::matching()` criteria expressions to behave more like in Twig commit f3c559f1c7f4b0e1390a42a5026b6e77ad4efbc3 Author: Matias Griese Date: Thu Jul 5 10:13:36 2018 +0300 Composer update (Fixes blueprint issues) commit 48a3228efdf97548d0b4ad8f3a18ec8ac8cc6fb6 Merge: be661e86 d2140809 Author: Matias Griese Date: Thu Jul 5 10:07:14 2018 +0300 Merge remote-tracking branch 'origin/1.5' into 1.5 # Conflicts: # CHANGELOG.md commit be661e868584beeb492f6538e3e965c5a9a6d670 Merge: 68a95528 a0918dfc Author: Matias Griese Date: Thu Jul 5 10:06:27 2018 +0300 Merge branch 'develop' of https://github.com/getgrav/grav into 1.5 # Conflicts: # CHANGELOG.md commit a0918dfc4f3a95c3f6a4950e790e9eea98e5bd99 Author: Timothy Cyrus Date: Mon Jul 2 18:46:44 2018 -0400 Update Media.php (#2083) Fixes getgrav/grav-plugin-admin#1330 commit d2140809746ae61f181fc57035ee454a273c78a5 Merge: 68a95528 a09c6b10 Author: Andy Miller Date: Mon Jul 2 16:05:56 2018 -0600 Merge branch 'develop' into 1.5 # Conflicts: # CHANGELOG.md commit a09c6b1088c13c85511eb415fb86eba815ad2856 Author: Andy Miller Date: Fri Jun 22 11:59:22 2018 +0100 Fix for Page::routeCanonical accpeting string #2069 commit dfed333e1b1c36afa6e74b7a62c332057bbdb7ac Author: Andy Miller Date: Wed Jun 20 23:38:41 2018 +0100 Set `html` in admin not in hereā€¦ commit 578e12940b22cd0b8e3ad11e3aff000642ff24e8 Author: Andy Miller Date: Wed Jun 20 22:56:27 2018 +0100 Fix for modular page preview #2066 commit 7d215f95cff2751ac05e443fe626150117b85c40 Merge: d72eca7f 5435ee60 Author: Andy Miller Date: Wed Jun 20 19:38:40 2018 +0100 Merge tag '1.4.6' into develop Release v1.4.6 commit 5435ee60d8d7859b216923c60b9380db507d83f7 Merge: 34cc3781 761d7927 Author: Andy Miller Date: Wed Jun 20 19:38:39 2018 +0100 Merge branch 'release/1.4.6' commit 761d79272cba304723f96f1c1bca49b1e93ae6b5 Author: Andy Miller Date: Wed Jun 20 19:37:15 2018 +0100 Prepare for release commit 68a9552877520ff9fa4f668d5590b8411150bf79 Merge: 7aa688ec d72eca7f Author: Matias Griese Date: Wed Jun 20 11:56:55 2018 +0300 Merge branch 'develop' of https://github.com/getgrav/grav into 1.5 # Conflicts: # composer.json # composer.lock commit d72eca7fb5a245a3356e3fee3ff7680ce31a274b Author: Matias Griese Date: Wed Jun 20 11:11:46 2018 +0300 Force Rockettheme/Toolbox to version 1.3.* in Grav 1.4 series commit ca9dba137233bc344e36a7685e96ea124d345a29 Author: Matias Griese Date: Wed Jun 20 10:57:57 2018 +0300 Move wrongly placed items in changelog to the latest release commit 7aa688ecbb280bd3fa3b5ec61677c09480a6b5c5 Merge: 794db2e3 4eb98664 Author: Matias Griese Date: Wed Jun 20 10:51:52 2018 +0300 Merge branch 'develop' of https://github.com/getgrav/grav into 1.5 # Conflicts: # CHANGELOG.md # system/defines.php commit 794db2e3e5000868de85fdc5b5f0cadad8af8059 Author: Andy Miller Date: Tue Jun 19 19:15:22 2018 +0100 set version as beta.1 commit ba457f7bf399774bdb75a1aff7f0ed98be5a6800 Author: Andy Miller Date: Tue Jun 19 19:11:22 2018 +0100 composer update commit 64715573a1ebbfc292f0b99dad97a32a854c47a7 Author: Andy Miller Date: Tue Jun 19 18:46:42 2018 +0100 Update changelog commit 82885515318a39638ea77726324e4a303ffb3bc5 Author: Andy Miller Date: Tue Jun 19 18:42:55 2018 +0100 Merge tag '1.4.6' into develop Release v1.4.6 # Conflicts: # system/defines.php commit 9816b538f9592ce125e405a259ed6bee0595dbd4 Merge: bbfbdec4 bd21b7f9 Author: Matias Griese Date: Fri Jun 15 09:22:46 2018 +0300 Merge branch 'develop' of https://github.com/getgrav/grav into 1.5 # Conflicts: # composer.lock commit bbfbdec483583514ed4afe3ec34047d9810ac29c Author: Matias Griese Date: Thu Jun 14 19:23:35 2018 +0300 Added setting to disable sessions from the site [#2013] commit 9ca427e36947ab21236c2b02f655640db93f986c Author: Matias Griese Date: Thu Jun 14 12:46:27 2018 +0300 Composer update (rockettheme/toolbox => 1.4) commit 228757a5baf12fc582df52d4e8b07c3f2fec8fe8 Author: Matias Griese Date: Tue Jun 12 13:06:58 2018 +0300 Composer update (rockettheme/toolbox => dev-develop) commit 027a760ce20b635cab10a6d19901d03f14a16d8c Author: Matias Griese Date: Tue Jun 12 12:06:14 2018 +0300 Added function Session::clear() commit 0a3cadc6b23ecab0846dbfcf7dc0aa4ad2905291 Author: Matias Griese Date: Tue Jun 12 10:55:38 2018 +0300 Composer update commit 04ea06928066ab3af82a74b90a1e39042b3c483d Author: Matias Griese Date: Tue Jun 12 10:48:13 2018 +0300 Fixed routing issues with multisite and multilanguage (Grav 1.5 edition) (#2046) * Fixed routing issues with multisite and multilanguage (#1501) commit 280d54057cc82fe9df635c702a0ef35e7183f28c Merge: 9f753415 036fc2d2 Author: Matias Griese Date: Tue Jun 12 10:40:42 2018 +0300 Merge branch 'develop' of https://github.com/getgrav/grav into 1.5 commit 9f7534153e42a014ad4b8a7cae3349f28214fd46 Merge: 62a8d8b2 3b4296c7 Author: Matias Griese Date: Fri Jun 1 12:37:13 2018 +0300 Merge branch 'develop' of https://github.com/getgrav/grav into 1.5 # Conflicts: # CHANGELOG.md commit 62a8d8b2033989e654470956ba967c591b5639cc Author: Matias Griese Date: Thu May 31 20:04:16 2018 +0300 Add method MediaInterface::getMediaOrder() and implement it commit d7bd0bf1df39006e17735dd78daee3f8a2884fe4 Merge: 3ccadded 9eded2ef Author: Matias Griese Date: Thu May 31 12:28:19 2018 +0300 Merge branch 'develop' of https://github.com/getgrav/grav into 1.5 # Conflicts: # CHANGELOG.md commit 3ccadded973070170fb78dceb9da20067ec435ff Author: Matias Griese Date: Tue May 22 10:42:30 2018 +0300 Fixed blueprint field validation: Allow numeric inputs in text fields commit b82c17ea560d7308b7925eac3499b71e7aa1fb3c Author: Andy Miller Date: Sun May 20 12:01:38 2018 -0600 Fixed typo in trucate #1943 commit a0946c67b91f11b284a1fc63e85442ae8817b3ee Author: Matias Griese Date: Thu May 17 10:46:13 2018 +0300 Implement SessionInterface commit 00376d31182223b7d961ebffae64d74320751a50 Author: Matias Griese Date: Thu May 17 10:22:14 2018 +0300 Session code cleanup, add changelog entries commit e8fd5405a76ae3c3efcb0c8f918b562ca35699ec Merge: 718dfa9b eae017a3 Author: Matias Griese Date: Thu May 17 10:14:10 2018 +0300 Merge branch 'feature/session' of https://github.com/getgrav/grav into 1.5 commit eae017a30a83062332f83f7474130123c7c48a4e Merge: 78ab2aa4 19764719 Author: Matias Griese Date: Thu May 17 10:10:16 2018 +0300 Merge branch 'develop' of https://github.com/getgrav/grav into feature/session commit 718dfa9b5de3ea7af1e1d21a6c0f85b01d447a9a Merge: 11266ce8 19764719 Author: Andy Miller Date: Tue May 15 17:55:50 2018 -0600 Merge branch 'develop' into 1.5 # Conflicts: # CHANGELOG.md # system/defines.php # system/src/Grav/Common/User/User.php commit 11266ce8f8a206f2d9dde410da40b3cbdd4b49fd Author: Andy Miller Date: Mon May 14 18:25:36 2018 -0600 Revert "Add special handling for User authenticated and authorized properties" This reverts commit 8e0e3e871849a14cbaef6254fed34e49534e9625. commit dab595f571a4d79819ce20c2e6c78f78894be14e Merge: 5ab956a8 afe72d07 Author: Matias Griese Date: Mon May 14 21:25:35 2018 +0300 Merge branch 'develop' of https://github.com/getgrav/grav into 1.5 # Conflicts: # system/defines.php commit 5ab956a8ecbd9760e6bf5534053f0d05cd4bd967 Author: Matias Griese Date: Mon May 14 12:30:30 2018 +0300 Fixed `Route::withQueryParam()` to accept array values commit 2c82e15fa1f533dbed7b9966aae41c4ed3d08017 Author: Matias Griese Date: Thu May 10 19:56:56 2018 +0300 Added authorized support (2FA) commit 2c7d8667242c990a549eb209e234bf0af673d638 Author: Andy Miller Date: Wed May 9 22:43:26 2018 -0600 alignment of parens commit a977023e45c7bf28a4ae131859f78f19b0fe9a68 Author: Andy Miller Date: Wed May 9 14:43:28 2018 -0600 minor vendor updates commit 9c7008e225b389e5be72c887378011139af3dc37 Author: Djamil Legato Date: Wed May 9 10:41:23 2018 -0700 Enable markdown support for 2FA account field description commit 342aa0ff04c891153fe59adb6b437443780feee9 Author: Matias Griese Date: Wed May 9 14:01:20 2018 +0300 Added MediaTrait::getMediaUri() commit d434d51d426f7313a43064687c9239169662e67d Merge: f03eb693 18928d69 Author: Matias Griese Date: Tue May 8 21:00:24 2018 +0300 Merge remote-tracking branch 'origin/1.5' into 1.5 commit f03eb693e6f0db82d2523dee9a4452d931989349 Author: Matias Griese Date: Tue May 8 21:00:16 2018 +0300 Rename object key back to _key commit 18928d6962aa8838490d9ec8e11ecaa84780b8e2 Author: Andy Miller Date: Tue May 8 09:48:56 2018 -0600 Revert "fire onPluginsInitialized event" This reverts commit f7832e78dc47b4e4943815fc7c8fb29fa567f99c. commit f7832e78dc47b4e4943815fc7c8fb29fa567f99c Author: Andy Miller Date: Mon May 7 21:32:00 2018 -0600 fire onPluginsInitialized event commit 68428a714a84d5e1e3f904f32d5a64d899b17598 Author: Matias Griese Date: Mon May 7 15:24:43 2018 +0300 Change exception type in ContentBlock::fromArray() commit 58db31a7d8e8f76672f97e983be534f2770077ae Author: Matias Griese Date: Mon May 7 14:58:50 2018 +0300 Added support for ContentBlock checksums commit 2917345b56af69d55d2236c328d810c9c909fe45 Author: Matias Griese Date: Fri May 4 21:43:12 2018 +0300 Improve ObjectTrait commit 1cfd3482bbcf40b870e8d0d947406fcbf3c1fba0 Author: Matias Griese Date: Fri May 4 19:06:37 2018 +0300 Added `Grav\Common\Media` interfaces and trait; use those in `Page` and `Media` classes Added `Grav\Common\Page` interface to allow custom page types in the future commit 4d690938a87a8e0449472ceca67cff9cfce9cae6 Merge: 0b1c18d6 40b475ec Author: Matias Griese Date: Mon Apr 30 12:37:01 2018 +0300 Merge branch 'develop' of https://github.com/getgrav/grav into 1.5 commit 0b1c18d63e03c90d4f3fe38580a0908217210bd8 Author: Andy Miller Date: Fri Apr 27 15:40:03 2018 -0600 Added `Uri::post()` and `Uri::getConentType()` commit f681f1c60b11b873bb904ea1167f73fc7f6b288c Author: Andy Miller Date: Fri Apr 27 16:18:16 2018 -0600 resolve streams earlier commit 27df27d1df55c72ac70db555615300ff3ccaa07a Author: Andy Miller Date: Fri Apr 27 16:17:52 2018 -0600 resolve streams earlier commit 91e98cd32ead99f7164b9e9d77d374a615191de6 Author: Matias Griese Date: Fri Apr 27 20:41:05 2018 +0300 Added `RouteFactory::createFromString()` rename MarkdownFormatter configuration option commit 1cef2a182a8abca1f4c7f52f989a34d9953a92e2 Author: Matias Griese Date: Fri Apr 27 20:38:57 2018 +0300 Added `Grav\Common\Media` interfaces and trait; use those in `Page` and `Media` classes Added `Grav\Common\Page` interface to allow custom page types in the future commit ad8764897a1ccf11a22cc325b1607e3cb982239b Author: Matias Griese Date: Tue Apr 24 15:47:31 2018 +0300 New classes have wrong namespace commit d2e700eea2a21883b04c9501df01b952a134c570 Author: Matias Griese Date: Tue Apr 24 12:22:06 2018 +0300 Formatters: Better error handling, allow custom file extension commit 895e145d822a47e509175c3e342d7c6849b8c484 Author: Matias Griese Date: Tue Apr 24 11:41:55 2018 +0300 Added new `Grav\Framework\File\Formatter` classes for encoding/decoding YAML, MarkDown, JSON, INI and PHP serialized formats commit 78ab2aa47662005fd9561da4a7893e1fd0d1eb2f Author: Matias Griese Date: Mon Apr 23 09:40:31 2018 +0300 Minor improvement on session check (using PHP 5.4+ way) commit b9a7341e5e4e48e92547fb417dce54aa3daa807f Author: Matias Griese Date: Sun Apr 22 19:09:05 2018 +0300 Composer update to fix YAML issues commit 261ea624720f61bd5638a458653f599836ce2d43 Author: Matias Griese Date: Fri Apr 20 14:03:49 2018 +0300 Added compatibility mode to fall back to Symfony YAML 2.8 when needed commit fae2aa4582d1a1268827c4638e7bb375be46f73d Author: Matias Griese Date: Fri Apr 20 11:18:01 2018 +0300 Added compatibility mode to fall back to Symfony YAML 2.8 when needed commit fb7230ec9a8315284953e2234254e9f9b47137bf Merge: ce1e6354 692aff3c Author: Matias Griese Date: Fri Apr 20 10:24:06 2018 +0300 Merge branch 'develop' of https://github.com/getgrav/grav into 1.5 # Conflicts: # CHANGELOG.md commit 830c9524ebacfbc846dab379f1b4b100cbafbda4 Merge: b8c61e34 33cfa178 Author: Matias Griese Date: Mon Apr 16 09:33:07 2018 +0300 Merge branch 'develop' of https://github.com/getgrav/grav into feature/session commit ce1e6354518a4669a9dc4eed71b5c256c8091b06 Author: Matias Griese Date: Sat Apr 14 17:42:13 2018 +0300 Minimum PHP requirement was 5.6.4, not 5.6.0 commit 8b0c1b7937310a04e2e833483b9c3aac377285d1 Author: Matias Griese Date: Sat Apr 14 13:23:54 2018 +0300 Updated Symfony Components to 3.4, causing some compatibility issues with YAML files commit 87b0d80de0f51bfa1c16cc208274ba62738196bb Author: Matias Griese Date: Sat Apr 14 12:28:00 2018 +0300 PHP 5.6.0 minimum, updated to Doctrine Collections 1.4 commit b8c61e34c9016c76a162d823ba4cadcc6c3ed9f2 Author: Matias Griese Date: Sat Apr 14 11:50:22 2018 +0300 Improve session error if it fails to start commit 02555ba3f5159b7027f3d9f67249414d35d3db2c Merge: 2b17bf70 4db2b756 Author: Matias Griese Date: Fri Apr 13 21:04:40 2018 +0300 Merge branch 'develop' of https://github.com/getgrav/grav into feature/session commit 2b17bf70de14da86c5c495111684b55aaa8460c9 Merge: f31f7f09 8e019b79 Author: Matias Griese Date: Tue Apr 10 09:47:59 2018 +0300 Merge branch 'develop' of https://github.com/getgrav/grav into feature/session commit f31f7f0962f5a4f898c1fef501fe2c9f4fac91cc Author: Matias Griese Date: Wed Mar 21 12:45:57 2018 +0200 Added `Grav\Framework\Session` class to replace `RocketTheme\Toolbox\Session\Session` Improved session handling, allow all session configuration options in `system.session.options` --- .travis.yml | 1 - CHANGELOG.md | 63 ++- README.md | 2 +- composer.json | 16 +- composer.lock | 414 ++++++++++-------- index.php | 4 +- system/blueprints/config/system.yaml | 36 ++ system/blueprints/user/account.yaml | 1 + system/config/system.yaml | 6 +- system/defines.php | 4 +- .../Grav/Common/Config/ConfigFileFinder.php | 2 +- system/src/Grav/Common/Config/Setup.php | 26 +- system/src/Grav/Common/Data/Validation.php | 26 +- system/src/Grav/Common/Debugger.php | 9 +- system/src/Grav/Common/File/CompiledFile.php | 3 - system/src/Grav/Common/GPM/GPM.php | 11 +- system/src/Grav/Common/GPM/Installer.php | 6 +- system/src/Grav/Common/GPM/Licenses.php | 2 +- system/src/Grav/Common/Grav.php | 10 +- system/src/Grav/Common/Inflector.php | 7 +- .../Interfaces/MediaCollectionInterface.php | 9 + .../Media/Interfaces/MediaInterface.php | 29 ++ .../Media/Interfaces/MediaObjectInterface.php | 9 + .../Grav/Common/Media/Traits/MediaTrait.php | 112 +++++ .../Common/Page/Interfaces/PageInterface.php | 9 + system/src/Grav/Common/Page/Media.php | 8 +- .../Grav/Common/Page/Medium/AbstractMedia.php | 31 +- .../Grav/Common/Page/Medium/ImageMedium.php | 11 +- system/src/Grav/Common/Page/Medium/Medium.php | 11 +- .../Grav/Common/Page/Medium/VideoMedium.php | 34 ++ system/src/Grav/Common/Page/Page.php | 112 ++--- system/src/Grav/Common/Page/Pages.php | 72 +-- system/src/Grav/Common/Plugins.php | 2 +- .../Common/Processors/InitializeProcessor.php | 29 +- .../Common/Service/ConfigServiceProvider.php | 14 +- .../Common/Service/PageServiceProvider.php | 37 +- .../Common/Service/SessionServiceProvider.php | 47 +- system/src/Grav/Common/Session.php | 55 +-- system/src/Grav/Common/Themes.php | 2 +- .../Common/Twig/Node/TwigNodeMarkdown.php | 2 +- .../Grav/Common/Twig/Node/TwigNodeSwitch.php | 14 +- .../TokenParser/TwigTokenParserSwitch.php | 73 ++- system/src/Grav/Common/Twig/Twig.php | 7 +- system/src/Grav/Common/Twig/TwigExtension.php | 102 +++-- system/src/Grav/Common/Uri.php | 66 +-- system/src/Grav/Common/Utils.php | 90 ++-- .../src/Grav/Console/Cli/InstallCommand.php | 38 +- system/src/Grav/Console/ConsoleTrait.php | 6 +- .../src/Grav/Console/Gpm/InstallCommand.php | 15 +- .../src/Grav/Console/Gpm/VersionCommand.php | 7 +- .../src/Grav/Framework/Cache/CacheTrait.php | 27 +- .../Framework/Collection/ArrayCollection.php | 10 - .../Framework/ContentBlock/ContentBlock.php | 34 +- .../ContentBlock/ContentBlockInterface.php | 11 + .../Grav/Framework/ContentBlock/HtmlBlock.php | 1 + .../File/Formatter/FormatterInterface.php | 44 ++ .../Framework/File/Formatter/IniFormatter.php | 83 ++++ .../File/Formatter/JsonFormatter.php | 78 ++++ .../File/Formatter/MarkdownFormatter.php | 116 +++++ .../File/Formatter/SerializeFormatter.php | 96 ++++ .../File/Formatter/YamlFormatter.php | 103 +++++ .../Object/Base/ObjectCollectionTrait.php | 12 +- .../Framework/Object/Base/ObjectTrait.php | 29 +- .../Collection/ObjectExpressionVisitor.php | 198 +++++++++ .../Interfaces/ObjectCollectionInterface.php | 3 +- .../Framework/Object/ObjectCollection.php | 35 ++ .../Object/Property/ObjectPropertyTrait.php | 8 +- system/src/Grav/Framework/Route/Route.php | 9 +- .../src/Grav/Framework/Route/RouteFactory.php | 17 + system/src/Grav/Framework/Session/Session.php | 340 ++++++++++++++ .../Framework/Session/SessionInterface.php | 147 +++++++ tests/unit/Grav/Common/UtilsTest.php | 10 +- user/pages/02.typography/default.md | 2 +- webserver-configs/nginx-ddev-site.conf | 118 +++++ webserver-configs/nginx.conf | 2 +- 75 files changed, 2561 insertions(+), 674 deletions(-) create mode 100644 system/src/Grav/Common/Media/Interfaces/MediaCollectionInterface.php create mode 100644 system/src/Grav/Common/Media/Interfaces/MediaInterface.php create mode 100644 system/src/Grav/Common/Media/Interfaces/MediaObjectInterface.php create mode 100644 system/src/Grav/Common/Media/Traits/MediaTrait.php create mode 100644 system/src/Grav/Common/Page/Interfaces/PageInterface.php create mode 100644 system/src/Grav/Framework/File/Formatter/FormatterInterface.php create mode 100644 system/src/Grav/Framework/File/Formatter/IniFormatter.php create mode 100644 system/src/Grav/Framework/File/Formatter/JsonFormatter.php create mode 100644 system/src/Grav/Framework/File/Formatter/MarkdownFormatter.php create mode 100644 system/src/Grav/Framework/File/Formatter/SerializeFormatter.php create mode 100644 system/src/Grav/Framework/File/Formatter/YamlFormatter.php create mode 100644 system/src/Grav/Framework/Object/Collection/ObjectExpressionVisitor.php create mode 100644 system/src/Grav/Framework/Session/Session.php create mode 100644 system/src/Grav/Framework/Session/SessionInterface.php create mode 100644 webserver-configs/nginx-ddev-site.conf diff --git a/.travis.yml b/.travis.yml index 08127e7a2a..19df5390d7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,5 @@ language: php php: - - '5.5' - '5.6' - '7.0.21' - '7.1' diff --git a/CHANGELOG.md b/CHANGELOG.md index 93342d1dd7..16feced8de 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,63 @@ +# v1.5.0 +## 08/17/2018 + +1. [](#new) + * Set minimum requirements to [PHP 5.6.4](https://getgrav.org/blog/raising-php-requirements-2018) + * Updated Doctrine Collections to 1.4 + * Updated Symfony Components to 3.4 (with compatibility mode to fall back to Symfony YAML 2.8) + * Added `Uri::method()` to get current HTTP method (GET/POST etc) + * `FormatterInterface`: Added `getSupportedFileExtensions()` and `getDefaultFileExtension()` methods + * Added option to disable `SimpleCache` key validation + * Added support for multiple repo locations for `bin/grav install` command + * Added twig filters for casting values: `|string`, `|int`, `|bool`, `|float`, `|array` + * Made `ObjectCollection::matching()` criteria expressions to behave more like in Twig + * Criteria: Added support for `LENGTH()`, `LOWER()`, `UPPER()`, `LTRIM()`, `RTRIM()` and `TRIM()` + * Added `Grav\Framework\File\Formatter` classes for encoding/decoding YAML, Markdown, JSON, INI and PHP serialized strings + * Added `Grav\Framework\Session` class to replace `RocketTheme\Toolbox\Session\Session` + * Added `Grav\Common\Media` interfaces and trait; use those in `Page` and `Media` classes + * Added `Grav\Common\Page` interface to allow custom page types in the future + * Added setting to disable sessions from the site [#2013](https://github.com/getgrav/grav/issues/2013) + * Added new `strict_mode` settings in `system.yaml` for compatibility +1. [](#improved) + * Improved `Utils::url()` to support query strings + * Display better exception message if Grav fails to initialize + * Added `muted` and `playsinline` support to videos [#2124](https://github.com/getgrav/grav/pull/2124) + * Added `MediaTrait::clearMediaCache()` to allow cache to be cleared + * Added `MediaTrait::getMediaCache()` to allow custom caching + * Improved session handling, allow all session configuration options in `system.session.options` +1. [](#bugfix) + * Fix broken form nonce logic [#2121](https://github.com/getgrav/grav/pull/2121) + * Fixed issue with uppercase extensions and fallback media URLs [#2133](https://github.com/getgrav/grav/issues/2133) + * Fixed theme inheritance issue with `camel-case` that includes numbers [#2134](https://github.com/getgrav/grav/issues/2134) + * Typo in demo typography page [#2136](https://github.com/getgrav/grav/pull/2136) + * Fix for incorrect plugin order in debugger panel + * Made `|markdown` filter HTML safe + * Fixed bug in `ContentBlock` serialization + * Fixed `Route::withQueryParam()` to accept array values + * Fixed typo in truncate function [#1943](https://github.com/getgrav/grav/issues/1943) + * Fixed blueprint field validation: Allow numeric inputs in text fields + +# v1.4.8 +## 07/31/2018 + +1. [](#improved) + * Add Grav version to debug bar messages tab [#2106](https://github.com/getgrav/grav/pull/2106) + * Add Nginx config for ddev project to `webserver-configs` [#2117](https://github.com/getgrav/grav/pull/2117) + * Vendor library updates +1. [](#bugfix) + * Don't allow `null` to be set as Page content + +# v1.4.7 +## 07/13/2018 + +1. [](#improved) + * Use `getFilename` instead of `getBasename` [#2087](https://github.com/getgrav/grav/issues/2087) +1. [](#bugfix) + * Fix for modular page preview [#2066](https://github.com/getgrav/grav/issues/2066) + * `Page::routeCanonical()` should be string not array [#2069](https://github.com/getgrav/grav/issues/2069) + # v1.4.6 -## 06/19/2018 +## 06/20/2018 1. [](#improved) * Manually re-added the improved SSL off-loading that was lost with Grav v1.4.0 merge [#1888](https://github.com/getgrav/grav/pull/1888) @@ -13,6 +71,8 @@ * Fix classes on non-http based protocol links [#2034](https://github.com/getgrav/grav/issues/2034) * Fixed crash on IIS (Windows) with open_basedir in effect [#2053](https://github.com/getgrav/grav/issues/2053) * Fixed incorrect routing with setup.php based base [#1892](https://github.com/getgrav/grav/issues/1892) + * Fixed image resource memory deallocation [#2045](https://github.com/getgrav/grav/pull/2045) + * Fixed issue with Errors `display:` option not handling integers properly [admin#1452](https://github.com/getgrav/grav-plugin-admin/issues/1452) # v1.4.5 ## 05/15/2018 @@ -21,7 +81,6 @@ * Fixed an issue with some users getting **2FA** prompt after upgrade [admin#1442](https://github.com/getgrav/grav-plugin-admin/issues/1442) * Do not crash when generating URLs with arrays as parameters [#2018](https://github.com/getgrav/grav/pull/2018) * Utils::truncateHTML removes whitespace when generating summaries [#2004](https://github.com/getgrav/grav/pull/2004) - * Fixed issue with Errors `display:` option not handling integers properly [admin#1452](https://github.com/getgrav/grav-plugin-admin/issues/1452) # v1.4.4 ## 05/11/2018 diff --git a/README.md b/README.md index 916421930f..5afbe43657 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ The underlying architecture of Grav is designed to use well-established and _bes # Requirements -- PHP 5.5.9 or higher. Check the [required modules list](https://learn.getgrav.org/basics/requirements#php-requirements) +- PHP 5.6.4 or higher. Check the [required modules list](https://learn.getgrav.org/basics/requirements#php-requirements) - Check the [Apache](https://learn.getgrav.org/basics/requirements#apache-requirements) or [IIS](https://learn.getgrav.org/basics/requirements#iis-requirements) requirements # QuickStart diff --git a/composer.json b/composer.json index 9918530bfc..fbd5106f72 100644 --- a/composer.json +++ b/composer.json @@ -6,17 +6,17 @@ "homepage": "http://getgrav.org", "license": "MIT", "require": { - "php": ">=5.5.9", + "php": ">=5.6.4", "twig/twig": "~1.24", "erusev/parsedown": "1.6.4", "erusev/parsedown-extra": "~0.7", - "symfony/yaml": "~2.8", - "symfony/console": "~2.8", - "symfony/event-dispatcher": "~2.8", - "symfony/var-dumper": "~2.8", + "symfony/yaml": "~3.4", + "symfony/console": "~3.4", + "symfony/event-dispatcher": "~3.4", + "symfony/var-dumper": "~3.4", "symfony/polyfill-iconv": "~1.0", "doctrine/cache": "^1.6", - "doctrine/collections": "1.3", + "doctrine/collections": "^1.4", "psr/simple-cache": "^1.0", "psr/http-message": "^1.0", "guzzlehttp/psr7": "^1.4", @@ -26,7 +26,7 @@ "gregwar/image": "2.*", "donatj/phpuseragentparser": "~0.3", "pimple/pimple": "~3.2", - "rockettheme/toolbox": "~1.3", + "rockettheme/toolbox": "~1.4", "maximebf/debugbar": "~1.10", "ext-mbstring": "*", "ext-openssl": "*", @@ -45,7 +45,7 @@ }, "config": { "platform": { - "php": "5.5.9" + "php": "5.6.4" } }, "repositories": [ diff --git a/composer.lock b/composer.lock index cb8fe41297..d1e051c567 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "c71dffc7daccd08aba7a52a476569d4c", + "content-hash": "ec4860b0ab68318d0e4550d58b5c12b3", "packages": [ { "name": "antoligy/dom-string-iterators", @@ -52,16 +52,16 @@ }, { "name": "composer/ca-bundle", - "version": "1.1.1", + "version": "1.1.2", "source": { "type": "git", "url": "https://github.com/composer/ca-bundle.git", - "reference": "d2c0a83b7533d6912e8d516756ebd34f893e9169" + "reference": "46afded9720f40b9dc63542af4e3e43a1177acb0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/ca-bundle/zipball/d2c0a83b7533d6912e8d516756ebd34f893e9169", - "reference": "d2c0a83b7533d6912e8d516756ebd34f893e9169", + "url": "https://api.github.com/repos/composer/ca-bundle/zipball/46afded9720f40b9dc63542af4e3e43a1177acb0", + "reference": "46afded9720f40b9dc63542af4e3e43a1177acb0", "shasum": "" }, "require": { @@ -104,7 +104,7 @@ "ssl", "tls" ], - "time": "2018-03-29T19:57:20+00:00" + "time": "2018-08-08T08:57:40+00:00" }, { "name": "doctrine/cache", @@ -178,28 +178,29 @@ }, { "name": "doctrine/collections", - "version": "v1.3.0", + "version": "v1.4.0", "source": { "type": "git", "url": "https://github.com/doctrine/collections.git", - "reference": "6c1e4eef75f310ea1b3e30945e9f06e652128b8a" + "reference": "1a4fb7e902202c33cce8c55989b945612943c2ba" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/collections/zipball/6c1e4eef75f310ea1b3e30945e9f06e652128b8a", - "reference": "6c1e4eef75f310ea1b3e30945e9f06e652128b8a", + "url": "https://api.github.com/repos/doctrine/collections/zipball/1a4fb7e902202c33cce8c55989b945612943c2ba", + "reference": "1a4fb7e902202c33cce8c55989b945612943c2ba", "shasum": "" }, "require": { - "php": ">=5.3.2" + "php": "^5.6 || ^7.0" }, "require-dev": { - "phpunit/phpunit": "~4.0" + "doctrine/coding-standard": "~0.1@dev", + "phpunit/phpunit": "^5.7" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.2.x-dev" + "dev-master": "1.3.x-dev" } }, "autoload": { @@ -240,20 +241,20 @@ "collections", "iterator" ], - "time": "2015-04-14T22:21:58+00:00" + "time": "2017-01-03T10:49:41+00:00" }, { "name": "donatj/phpuseragentparser", - "version": "v0.9.0", + "version": "v0.10.0", "source": { "type": "git", "url": "https://github.com/donatj/PhpUserAgent.git", - "reference": "ea79de6a18e52285e62cd75cf1cebe276ecaf503" + "reference": "9de58cc2a3e986bfee7f4cea3365c830b765cf65" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/donatj/PhpUserAgent/zipball/ea79de6a18e52285e62cd75cf1cebe276ecaf503", - "reference": "ea79de6a18e52285e62cd75cf1cebe276ecaf503", + "url": "https://api.github.com/repos/donatj/PhpUserAgent/zipball/9de58cc2a3e986bfee7f4cea3365c830b765cf65", + "reference": "9de58cc2a3e986bfee7f4cea3365c830b765cf65", "shasum": "" }, "require": { @@ -291,7 +292,7 @@ "user agent", "useragent" ], - "time": "2017-10-23T16:52:52+00:00" + "time": "2018-06-21T15:54:46+00:00" }, { "name": "erusev/parsedown", @@ -605,26 +606,30 @@ }, { "name": "league/climate", - "version": "3.2.4", + "version": "3.4.1", "source": { "type": "git", "url": "https://github.com/thephpleague/climate.git", - "reference": "ca70f67f7739cca823eba0ad98f8130bca226bf0" + "reference": "d657a19837c1f79a891381fb128b755aa3386381" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/climate/zipball/ca70f67f7739cca823eba0ad98f8130bca226bf0", - "reference": "ca70f67f7739cca823eba0ad98f8130bca226bf0", + "url": "https://api.github.com/repos/thephpleague/climate/zipball/d657a19837c1f79a891381fb128b755aa3386381", + "reference": "d657a19837c1f79a891381fb128b755aa3386381", "shasum": "" }, "require": { - "php": ">=5.4.0", - "seld/cli-prompt": "~1.0" + "php": "^5.6|^7.0", + "psr/log": "^1.0", + "seld/cli-prompt": "^1.0" }, "require-dev": { - "mikey179/vfsstream": "~1.4", - "mockery/mockery": "~0.9", - "phpunit/phpunit": "~4.6" + "mikey179/vfsstream": "^1.4", + "mockery/mockery": "^1.0", + "phpunit/phpunit": "^5.7.16" + }, + "suggest": { + "ext-mbstring": "If ext-mbstring is not available you MUST install symfony/polyfill-mbstring" }, "type": "library", "autoload": { @@ -637,6 +642,12 @@ "MIT" ], "authors": [ + { + "name": "Craig Duncan", + "email": "git@duncanc.co.uk", + "homepage": "https://github.com/duncan3dc", + "role": "Developer" + }, { "name": "Joe Tannenbaum", "email": "hey@joe.codes", @@ -652,7 +663,7 @@ "php", "terminal" ], - "time": "2016-10-30T22:18:25+00:00" + "time": "2018-04-29T16:43:54+00:00" }, { "name": "matthiasmullie/minify", @@ -1203,16 +1214,16 @@ }, { "name": "rockettheme/toolbox", - "version": "1.4.0", + "version": "1.4.2", "source": { "type": "git", "url": "https://github.com/rockettheme/toolbox.git", - "reference": "ca12fcc980dc7bdf3f955beb74278a070448a485" + "reference": "93f5c3d5e173cee7419df20eed52711471abbc3e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/rockettheme/toolbox/zipball/ca12fcc980dc7bdf3f955beb74278a070448a485", - "reference": "ca12fcc980dc7bdf3f955beb74278a070448a485", + "url": "https://api.github.com/repos/rockettheme/toolbox/zipball/93f5c3d5e173cee7419df20eed52711471abbc3e", + "reference": "93f5c3d5e173cee7419df20eed52711471abbc3e", "shasum": "" }, "require": { @@ -1248,7 +1259,7 @@ "php", "rockettheme" ], - "time": "2018-06-13T17:11:52+00:00" + "time": "2018-08-08T18:03:32+00:00" }, { "name": "seld/cli-prompt", @@ -1300,37 +1311,45 @@ }, { "name": "symfony/console", - "version": "v2.8.41", + "version": "v3.4.14", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "e8e59b74ad1274714dad2748349b55e3e6e630c7" + "reference": "6b217594552b9323bcdcfc14f8a0ce126e84cd73" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/e8e59b74ad1274714dad2748349b55e3e6e630c7", - "reference": "e8e59b74ad1274714dad2748349b55e3e6e630c7", + "url": "https://api.github.com/repos/symfony/console/zipball/6b217594552b9323bcdcfc14f8a0ce126e84cd73", + "reference": "6b217594552b9323bcdcfc14f8a0ce126e84cd73", "shasum": "" }, "require": { - "php": ">=5.3.9", - "symfony/debug": "^2.7.2|~3.0.0", + "php": "^5.5.9|>=7.0.8", + "symfony/debug": "~2.8|~3.0|~4.0", "symfony/polyfill-mbstring": "~1.0" }, + "conflict": { + "symfony/dependency-injection": "<3.4", + "symfony/process": "<3.3" + }, "require-dev": { "psr/log": "~1.0", - "symfony/event-dispatcher": "~2.1|~3.0.0", - "symfony/process": "~2.1|~3.0.0" + "symfony/config": "~3.3|~4.0", + "symfony/dependency-injection": "~3.4|~4.0", + "symfony/event-dispatcher": "~2.8|~3.0|~4.0", + "symfony/lock": "~3.4|~4.0", + "symfony/process": "~3.3|~4.0" }, "suggest": { "psr/log-implementation": "For using the console logger", "symfony/event-dispatcher": "", + "symfony/lock": "", "symfony/process": "" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.8-dev" + "dev-master": "3.4-dev" } }, "autoload": { @@ -1357,37 +1376,36 @@ ], "description": "Symfony Console Component", "homepage": "https://symfony.com", - "time": "2018-05-15T21:17:45+00:00" + "time": "2018-07-26T11:19:56+00:00" }, { "name": "symfony/debug", - "version": "v3.0.9", + "version": "v3.4.14", "source": { "type": "git", "url": "https://github.com/symfony/debug.git", - "reference": "697c527acd9ea1b2d3efac34d9806bf255278b0a" + "reference": "d5a058ff6ecad26b30c1ba452241306ea34c65cc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/debug/zipball/697c527acd9ea1b2d3efac34d9806bf255278b0a", - "reference": "697c527acd9ea1b2d3efac34d9806bf255278b0a", + "url": "https://api.github.com/repos/symfony/debug/zipball/d5a058ff6ecad26b30c1ba452241306ea34c65cc", + "reference": "d5a058ff6ecad26b30c1ba452241306ea34c65cc", "shasum": "" }, "require": { - "php": ">=5.5.9", + "php": "^5.5.9|>=7.0.8", "psr/log": "~1.0" }, "conflict": { "symfony/http-kernel": ">=2.3,<2.3.24|~2.4.0|>=2.5,<2.5.9|>=2.6,<2.6.2" }, "require-dev": { - "symfony/class-loader": "~2.8|~3.0", - "symfony/http-kernel": "~2.8|~3.0" + "symfony/http-kernel": "~2.8|~3.0|~4.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.0-dev" + "dev-master": "3.4-dev" } }, "autoload": { @@ -1414,31 +1432,34 @@ ], "description": "Symfony Debug Component", "homepage": "https://symfony.com", - "time": "2016-07-30T07:22:48+00:00" + "time": "2018-07-26T11:19:56+00:00" }, { "name": "symfony/event-dispatcher", - "version": "v2.8.41", + "version": "v3.4.14", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "9b69aad7d4c086dc94ebade2d5eb9145da5dac8c" + "reference": "b2e1f19280c09a42dc64c0b72b80fe44dd6e88fb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/9b69aad7d4c086dc94ebade2d5eb9145da5dac8c", - "reference": "9b69aad7d4c086dc94ebade2d5eb9145da5dac8c", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/b2e1f19280c09a42dc64c0b72b80fe44dd6e88fb", + "reference": "b2e1f19280c09a42dc64c0b72b80fe44dd6e88fb", "shasum": "" }, "require": { - "php": ">=5.3.9" + "php": "^5.5.9|>=7.0.8" + }, + "conflict": { + "symfony/dependency-injection": "<3.3" }, "require-dev": { "psr/log": "~1.0", - "symfony/config": "^2.0.5|~3.0.0", - "symfony/dependency-injection": "~2.6|~3.0.0", - "symfony/expression-language": "~2.6|~3.0.0", - "symfony/stopwatch": "~2.3|~3.0.0" + "symfony/config": "~2.8|~3.0|~4.0", + "symfony/dependency-injection": "~3.3|~4.0", + "symfony/expression-language": "~2.8|~3.0|~4.0", + "symfony/stopwatch": "~2.8|~3.0|~4.0" }, "suggest": { "symfony/dependency-injection": "", @@ -1447,7 +1468,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.8-dev" + "dev-master": "3.4-dev" } }, "autoload": { @@ -1474,29 +1495,32 @@ ], "description": "Symfony EventDispatcher Component", "homepage": "https://symfony.com", - "time": "2018-04-06T07:35:03+00:00" + "time": "2018-07-26T09:06:28+00:00" }, { "name": "symfony/polyfill-ctype", - "version": "v1.8.0", + "version": "v1.9.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "7cc359f1b7b80fc25ed7796be7d96adc9b354bae" + "reference": "e3d826245268269cd66f8326bd8bc066687b4a19" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/7cc359f1b7b80fc25ed7796be7d96adc9b354bae", - "reference": "7cc359f1b7b80fc25ed7796be7d96adc9b354bae", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/e3d826245268269cd66f8326bd8bc066687b4a19", + "reference": "e3d826245268269cd66f8326bd8bc066687b4a19", "shasum": "" }, "require": { "php": ">=5.3.3" }, + "suggest": { + "ext-ctype": "For best performance" + }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.8-dev" + "dev-master": "1.9-dev" } }, "autoload": { @@ -1529,20 +1553,20 @@ "polyfill", "portable" ], - "time": "2018-04-30T19:57:29+00:00" + "time": "2018-08-06T14:22:27+00:00" }, { "name": "symfony/polyfill-iconv", - "version": "v1.8.0", + "version": "v1.9.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-iconv.git", - "reference": "7cb8436a814d5b0fcf292810ee26f8b0cb47584d" + "reference": "bcc0cd69185b8a5d8b4a5400c489ed3333bf9bb2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-iconv/zipball/7cb8436a814d5b0fcf292810ee26f8b0cb47584d", - "reference": "7cb8436a814d5b0fcf292810ee26f8b0cb47584d", + "url": "https://api.github.com/repos/symfony/polyfill-iconv/zipball/bcc0cd69185b8a5d8b4a5400c489ed3333bf9bb2", + "reference": "bcc0cd69185b8a5d8b4a5400c489ed3333bf9bb2", "shasum": "" }, "require": { @@ -1554,7 +1578,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.8-dev" + "dev-master": "1.9-dev" } }, "autoload": { @@ -1588,20 +1612,20 @@ "portable", "shim" ], - "time": "2018-04-26T10:06:28+00:00" + "time": "2018-08-06T14:22:27+00:00" }, { "name": "symfony/polyfill-mbstring", - "version": "v1.8.0", + "version": "v1.9.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "3296adf6a6454a050679cde90f95350ad604b171" + "reference": "d0cd638f4634c16d8df4508e847f14e9e43168b8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/3296adf6a6454a050679cde90f95350ad604b171", - "reference": "3296adf6a6454a050679cde90f95350ad604b171", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/d0cd638f4634c16d8df4508e847f14e9e43168b8", + "reference": "d0cd638f4634c16d8df4508e847f14e9e43168b8", "shasum": "" }, "require": { @@ -1613,7 +1637,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.8-dev" + "dev-master": "1.9-dev" } }, "autoload": { @@ -1647,24 +1671,24 @@ "portable", "shim" ], - "time": "2018-04-26T10:06:28+00:00" + "time": "2018-08-06T14:22:27+00:00" }, { "name": "symfony/var-dumper", - "version": "v2.8.41", + "version": "v3.4.14", "source": { "type": "git", "url": "https://github.com/symfony/var-dumper.git", - "reference": "c3d7a096ccaba86e2fadeb444ca32fcbc5a31ebd" + "reference": "f62a394bd3de96f2f5e8f4c7d685035897fb3cb3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-dumper/zipball/c3d7a096ccaba86e2fadeb444ca32fcbc5a31ebd", - "reference": "c3d7a096ccaba86e2fadeb444ca32fcbc5a31ebd", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/f62a394bd3de96f2f5e8f4c7d685035897fb3cb3", + "reference": "f62a394bd3de96f2f5e8f4c7d685035897fb3cb3", "shasum": "" }, "require": { - "php": ">=5.3.9", + "php": "^5.5.9|>=7.0.8", "symfony/polyfill-mbstring": "~1.0" }, "conflict": { @@ -1676,12 +1700,13 @@ }, "suggest": { "ext-iconv": "To convert non-UTF-8 strings to UTF-8 (or symfony/polyfill-iconv in case ext-iconv cannot be used).", + "ext-intl": "To show region name in time zone dump", "ext-symfony_debug": "" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.8-dev" + "dev-master": "3.4-dev" } }, "autoload": { @@ -1715,30 +1740,39 @@ "debug", "dump" ], - "time": "2018-04-25T14:40:02+00:00" + "time": "2018-07-26T11:19:56+00:00" }, { "name": "symfony/yaml", - "version": "v2.8.41", + "version": "v3.4.14", "source": { "type": "git", "url": "https://github.com/symfony/yaml.git", - "reference": "51356b7a2ff7c9fd06b2f1681cc463bb62b5c1ff" + "reference": "810af2d35fc72b6cf5c01116806d2b65ccaaf2e2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/51356b7a2ff7c9fd06b2f1681cc463bb62b5c1ff", - "reference": "51356b7a2ff7c9fd06b2f1681cc463bb62b5c1ff", + "url": "https://api.github.com/repos/symfony/yaml/zipball/810af2d35fc72b6cf5c01116806d2b65ccaaf2e2", + "reference": "810af2d35fc72b6cf5c01116806d2b65ccaaf2e2", "shasum": "" }, "require": { - "php": ">=5.3.9", + "php": "^5.5.9|>=7.0.8", "symfony/polyfill-ctype": "~1.8" }, + "conflict": { + "symfony/console": "<3.4" + }, + "require-dev": { + "symfony/console": "~3.4|~4.0" + }, + "suggest": { + "symfony/console": "For validating YAML files using the lint command" + }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.8-dev" + "dev-master": "3.4-dev" } }, "autoload": { @@ -1765,24 +1799,25 @@ ], "description": "Symfony Yaml Component", "homepage": "https://symfony.com", - "time": "2018-05-01T22:52:40+00:00" + "time": "2018-07-26T11:19:56+00:00" }, { "name": "twig/twig", - "version": "v1.35.3", + "version": "v1.35.4", "source": { "type": "git", "url": "https://github.com/twigphp/Twig.git", - "reference": "b48680b6eb7d16b5025b9bfc4108d86f6b8af86f" + "reference": "7e081e98378a1e78c29cc9eba4aefa5d78a05d2a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/twigphp/Twig/zipball/b48680b6eb7d16b5025b9bfc4108d86f6b8af86f", - "reference": "b48680b6eb7d16b5025b9bfc4108d86f6b8af86f", + "url": "https://api.github.com/repos/twigphp/Twig/zipball/7e081e98378a1e78c29cc9eba4aefa5d78a05d2a", + "reference": "7e081e98378a1e78c29cc9eba4aefa5d78a05d2a", "shasum": "" }, "require": { - "php": ">=5.3.3" + "php": ">=5.3.3", + "symfony/polyfill-ctype": "^1.8" }, "require-dev": { "psr/container": "^1.0", @@ -1821,16 +1856,16 @@ }, { "name": "Twig Team", - "homepage": "http://twig.sensiolabs.org/contributors", + "homepage": "https://twig.symfony.com/contributors", "role": "Contributors" } ], "description": "Twig, the flexible, fast, and secure template language for PHP", - "homepage": "http://twig.sensiolabs.org", + "homepage": "https://twig.symfony.com", "keywords": [ "templating" ], - "time": "2018-03-20T04:25:58+00:00" + "time": "2018-07-13T07:12:17+00:00" } ], "packages-dev": [ @@ -1895,28 +1930,28 @@ }, { "name": "codeception/codeception", - "version": "2.4.1", + "version": "2.4.5", "source": { "type": "git", "url": "https://github.com/Codeception/Codeception.git", - "reference": "bca3547632556875f1cdd567d6057cc14fe472b8" + "reference": "5fee32d5c82791548931cbc34806b4de6aa1abfc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Codeception/Codeception/zipball/bca3547632556875f1cdd567d6057cc14fe472b8", - "reference": "bca3547632556875f1cdd567d6057cc14fe472b8", + "url": "https://api.github.com/repos/Codeception/Codeception/zipball/5fee32d5c82791548931cbc34806b4de6aa1abfc", + "reference": "5fee32d5c82791548931cbc34806b4de6aa1abfc", "shasum": "" }, "require": { "behat/gherkin": "^4.4.0", "codeception/phpunit-wrapper": "^6.0.9|^7.0.6", - "codeception/stub": "^1.0", + "codeception/stub": "^2.0", "ext-json": "*", "ext-mbstring": "*", "facebook/webdriver": ">=1.1.3 <2.0", "guzzlehttp/guzzle": ">=4.1.4 <7.0", "guzzlehttp/psr7": "~1.0", - "php": ">=5.4.0 <8.0", + "php": ">=5.6.0 <8.0", "symfony/browser-kit": ">=2.7 <5.0", "symfony/console": ">=2.7 <5.0", "symfony/css-selector": ">=2.7 <5.0", @@ -1982,20 +2017,20 @@ "functional testing", "unit testing" ], - "time": "2018-03-31T22:30:43+00:00" + "time": "2018-08-01T07:21:49+00:00" }, { "name": "codeception/phpunit-wrapper", - "version": "6.0.9", + "version": "6.0.10", "source": { "type": "git", "url": "https://github.com/Codeception/phpunit-wrapper.git", - "reference": "450f1cfc5f49539c421061e64338f5edb8baad6a" + "reference": "7057e599d97b02b4efb009681a43b327dbce138a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Codeception/phpunit-wrapper/zipball/450f1cfc5f49539c421061e64338f5edb8baad6a", - "reference": "450f1cfc5f49539c421061e64338f5edb8baad6a", + "url": "https://api.github.com/repos/Codeception/phpunit-wrapper/zipball/7057e599d97b02b4efb009681a43b327dbce138a", + "reference": "7057e599d97b02b4efb009681a43b327dbce138a", "shasum": "" }, "require": { @@ -2028,26 +2063,23 @@ } ], "description": "PHPUnit classes used by Codeception", - "time": "2018-03-31T18:50:01+00:00" + "time": "2018-06-20T20:08:14+00:00" }, { "name": "codeception/stub", - "version": "1.0.4", + "version": "2.0.4", "source": { "type": "git", "url": "https://github.com/Codeception/Stub.git", - "reference": "681b62348837a5ef07d10d8a226f5bc358cc8805" + "reference": "f50bc271f392a2836ff80690ce0c058efe1ae03e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Codeception/Stub/zipball/681b62348837a5ef07d10d8a226f5bc358cc8805", - "reference": "681b62348837a5ef07d10d8a226f5bc358cc8805", + "url": "https://api.github.com/repos/Codeception/Stub/zipball/f50bc271f392a2836ff80690ce0c058efe1ae03e", + "reference": "f50bc271f392a2836ff80690ce0c058efe1ae03e", "shasum": "" }, "require": { - "phpunit/phpunit-mock-objects": ">2.3 <7.0" - }, - "require-dev": { "phpunit/phpunit": ">=4.8 <8.0" }, "type": "library", @@ -2061,7 +2093,7 @@ "MIT" ], "description": "Flexible Stub wrapper for PHPUnit's Mock Builder", - "time": "2018-05-17T09:31:08+00:00" + "time": "2018-07-26T11:55:37+00:00" }, { "name": "doctrine/instantiator", @@ -2119,30 +2151,38 @@ }, { "name": "facebook/webdriver", - "version": "1.4.1", + "version": "1.6.0", "source": { "type": "git", "url": "https://github.com/facebook/php-webdriver.git", - "reference": "eadb0b7a7c3e6578185197fd40158b08c3164c83" + "reference": "bd8c740097eb9f2fc3735250fc1912bc811a954e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/facebook/php-webdriver/zipball/eadb0b7a7c3e6578185197fd40158b08c3164c83", - "reference": "eadb0b7a7c3e6578185197fd40158b08c3164c83", + "url": "https://api.github.com/repos/facebook/php-webdriver/zipball/bd8c740097eb9f2fc3735250fc1912bc811a954e", + "reference": "bd8c740097eb9f2fc3735250fc1912bc811a954e", "shasum": "" }, "require": { "ext-curl": "*", + "ext-json": "*", + "ext-mbstring": "*", "ext-zip": "*", - "php": "^5.5 || ~7.0", - "symfony/process": "^2.8 || ^3.1" + "php": "^5.6 || ~7.0", + "symfony/process": "^2.8 || ^3.1 || ^4.0" }, "require-dev": { "friendsofphp/php-cs-fixer": "^2.0", + "jakub-onderka/php-parallel-lint": "^0.9.2", + "php-coveralls/php-coveralls": "^2.0", "php-mock/php-mock-phpunit": "^1.1", - "phpunit/phpunit": "4.6.* || ~5.0", - "satooshi/php-coveralls": "^1.0", - "squizlabs/php_codesniffer": "^2.6" + "phpunit/phpunit": "^5.7", + "sebastian/environment": "^1.3.4 || ^2.0 || ^3.0", + "squizlabs/php_codesniffer": "^2.6", + "symfony/var-dumper": "^3.3 || ^4.0" + }, + "suggest": { + "ext-SimpleXML": "For Firefox profile creation" }, "type": "library", "extra": { @@ -2167,20 +2207,20 @@ "selenium", "webdriver" ], - "time": "2017-04-28T14:54:49+00:00" + "time": "2018-05-16T17:37:13+00:00" }, { "name": "fzaninotto/faker", - "version": "v1.7.1", + "version": "v1.8.0", "source": { "type": "git", "url": "https://github.com/fzaninotto/Faker.git", - "reference": "d3ed4cc37051c1ca52d22d76b437d14809fc7e0d" + "reference": "f72816b43e74063c8b10357394b6bba8cb1c10de" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/fzaninotto/Faker/zipball/d3ed4cc37051c1ca52d22d76b437d14809fc7e0d", - "reference": "d3ed4cc37051c1ca52d22d76b437d14809fc7e0d", + "url": "https://api.github.com/repos/fzaninotto/Faker/zipball/f72816b43e74063c8b10357394b6bba8cb1c10de", + "reference": "f72816b43e74063c8b10357394b6bba8cb1c10de", "shasum": "" }, "require": { @@ -2188,7 +2228,7 @@ }, "require-dev": { "ext-intl": "*", - "phpunit/phpunit": "^4.0 || ^5.0", + "phpunit/phpunit": "^4.8.35 || ^5.7", "squizlabs/php_codesniffer": "^1.5" }, "type": "library", @@ -2217,7 +2257,7 @@ "faker", "fixtures" ], - "time": "2017-08-15T16:48:10+00:00" + "time": "2018-07-12T10:23:15+00:00" }, { "name": "guzzlehttp/guzzle", @@ -2391,22 +2431,22 @@ }, { "name": "phpdocumentor/reflection-docblock", - "version": "3.2.2", + "version": "3.3.2", "source": { "type": "git", "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", - "reference": "4aada1f93c72c35e22fb1383b47fee43b8f1d157" + "reference": "bf329f6c1aadea3299f08ee804682b7c45b326a2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/4aada1f93c72c35e22fb1383b47fee43b8f1d157", - "reference": "4aada1f93c72c35e22fb1383b47fee43b8f1d157", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/bf329f6c1aadea3299f08ee804682b7c45b326a2", + "reference": "bf329f6c1aadea3299f08ee804682b7c45b326a2", "shasum": "" }, "require": { - "php": ">=5.5", - "phpdocumentor/reflection-common": "^1.0@dev", - "phpdocumentor/type-resolver": "^0.3.0", + "php": "^5.6 || ^7.0", + "phpdocumentor/reflection-common": "^1.0.0", + "phpdocumentor/type-resolver": "^0.4.0", "webmozart/assert": "^1.0" }, "require-dev": { @@ -2432,20 +2472,20 @@ } ], "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", - "time": "2017-08-08T06:39:58+00:00" + "time": "2017-11-10T14:09:06+00:00" }, { "name": "phpdocumentor/type-resolver", - "version": "0.3.0", + "version": "0.4.0", "source": { "type": "git", "url": "https://github.com/phpDocumentor/TypeResolver.git", - "reference": "fb3933512008d8162b3cdf9e18dba9309b7c3773" + "reference": "9c977708995954784726e25d0cd1dddf4e65b0f7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/fb3933512008d8162b3cdf9e18dba9309b7c3773", - "reference": "fb3933512008d8162b3cdf9e18dba9309b7c3773", + "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/9c977708995954784726e25d0cd1dddf4e65b0f7", + "reference": "9c977708995954784726e25d0cd1dddf4e65b0f7", "shasum": "" }, "require": { @@ -2479,20 +2519,20 @@ "email": "me@mikevanriel.com" } ], - "time": "2017-06-03T08:32:36+00:00" + "time": "2017-07-14T14:27:02+00:00" }, { "name": "phpspec/prophecy", - "version": "1.7.6", + "version": "1.8.0", "source": { "type": "git", "url": "https://github.com/phpspec/prophecy.git", - "reference": "33a7e3c4fda54e912ff6338c48823bd5c0f0b712" + "reference": "4ba436b55987b4bf311cb7c6ba82aa528aac0a06" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpspec/prophecy/zipball/33a7e3c4fda54e912ff6338c48823bd5c0f0b712", - "reference": "33a7e3c4fda54e912ff6338c48823bd5c0f0b712", + "url": "https://api.github.com/repos/phpspec/prophecy/zipball/4ba436b55987b4bf311cb7c6ba82aa528aac0a06", + "reference": "4ba436b55987b4bf311cb7c6ba82aa528aac0a06", "shasum": "" }, "require": { @@ -2504,12 +2544,12 @@ }, "require-dev": { "phpspec/phpspec": "^2.5|^3.2", - "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.5" + "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.5 || ^7.1" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.7.x-dev" + "dev-master": "1.8.x-dev" } }, "autoload": { @@ -2542,7 +2582,7 @@ "spy", "stub" ], - "time": "2018-04-18T13:57:24+00:00" + "time": "2018-08-05T17:53:17+00:00" }, { "name": "phpunit/php-code-coverage", @@ -3294,16 +3334,16 @@ }, { "name": "symfony/browser-kit", - "version": "v3.4.11", + "version": "v3.4.14", "source": { "type": "git", "url": "https://github.com/symfony/browser-kit.git", - "reference": "840bb6f0d5b3701fd768b68adf7193c2d0f98f79" + "reference": "f6668d1a6182d5a8dec65a1c863a4c1d963816c0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/browser-kit/zipball/840bb6f0d5b3701fd768b68adf7193c2d0f98f79", - "reference": "840bb6f0d5b3701fd768b68adf7193c2d0f98f79", + "url": "https://api.github.com/repos/symfony/browser-kit/zipball/f6668d1a6182d5a8dec65a1c863a4c1d963816c0", + "reference": "f6668d1a6182d5a8dec65a1c863a4c1d963816c0", "shasum": "" }, "require": { @@ -3347,20 +3387,20 @@ ], "description": "Symfony BrowserKit Component", "homepage": "https://symfony.com", - "time": "2018-03-19T22:32:39+00:00" + "time": "2018-07-26T09:06:28+00:00" }, { "name": "symfony/css-selector", - "version": "v3.4.11", + "version": "v3.4.14", "source": { "type": "git", "url": "https://github.com/symfony/css-selector.git", - "reference": "d2ce52290b648ae33b5301d09bc14ee378612914" + "reference": "edda5a6155000ff8c3a3f85ee5c421af93cca416" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/css-selector/zipball/d2ce52290b648ae33b5301d09bc14ee378612914", - "reference": "d2ce52290b648ae33b5301d09bc14ee378612914", + "url": "https://api.github.com/repos/symfony/css-selector/zipball/edda5a6155000ff8c3a3f85ee5c421af93cca416", + "reference": "edda5a6155000ff8c3a3f85ee5c421af93cca416", "shasum": "" }, "require": { @@ -3400,20 +3440,20 @@ ], "description": "Symfony CssSelector Component", "homepage": "https://symfony.com", - "time": "2018-05-16T12:49:49+00:00" + "time": "2018-07-26T09:06:28+00:00" }, { "name": "symfony/dom-crawler", - "version": "v3.4.11", + "version": "v3.4.14", "source": { "type": "git", "url": "https://github.com/symfony/dom-crawler.git", - "reference": "201b210fafcdd193c1e45b2994bf7133fb6263e8" + "reference": "452bfc854b60134438e3824b159b0d24a5892331" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/201b210fafcdd193c1e45b2994bf7133fb6263e8", - "reference": "201b210fafcdd193c1e45b2994bf7133fb6263e8", + "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/452bfc854b60134438e3824b159b0d24a5892331", + "reference": "452bfc854b60134438e3824b159b0d24a5892331", "shasum": "" }, "require": { @@ -3457,20 +3497,20 @@ ], "description": "Symfony DomCrawler Component", "homepage": "https://symfony.com", - "time": "2018-05-01T22:53:27+00:00" + "time": "2018-07-26T10:03:52+00:00" }, { "name": "symfony/finder", - "version": "v3.4.11", + "version": "v3.4.14", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "472a92f3df8b247b49ae364275fb32943b9656c6" + "reference": "8a84fcb207451df0013b2c74cbbf1b62d47b999a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/472a92f3df8b247b49ae364275fb32943b9656c6", - "reference": "472a92f3df8b247b49ae364275fb32943b9656c6", + "url": "https://api.github.com/repos/symfony/finder/zipball/8a84fcb207451df0013b2c74cbbf1b62d47b999a", + "reference": "8a84fcb207451df0013b2c74cbbf1b62d47b999a", "shasum": "" }, "require": { @@ -3506,20 +3546,20 @@ ], "description": "Symfony Finder Component", "homepage": "https://symfony.com", - "time": "2018-05-16T08:49:21+00:00" + "time": "2018-07-26T11:19:56+00:00" }, { "name": "symfony/process", - "version": "v3.4.11", + "version": "v3.4.14", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "4cbf2db9abcb01486a21b7a059e03a62fae63187" + "reference": "0414db29bd770ec5a4152683e655f55efd4fa60f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/4cbf2db9abcb01486a21b7a059e03a62fae63187", - "reference": "4cbf2db9abcb01486a21b7a059e03a62fae63187", + "url": "https://api.github.com/repos/symfony/process/zipball/0414db29bd770ec5a4152683e655f55efd4fa60f", + "reference": "0414db29bd770ec5a4152683e655f55efd4fa60f", "shasum": "" }, "require": { @@ -3555,7 +3595,7 @@ ], "description": "Symfony Process Component", "homepage": "https://symfony.com", - "time": "2018-05-16T08:49:21+00:00" + "time": "2018-07-26T11:19:56+00:00" }, { "name": "victorjonsson/markdowndocs", @@ -3662,7 +3702,7 @@ "prefer-stable": false, "prefer-lowest": false, "platform": { - "php": ">=5.5.9", + "php": ">=5.6.4", "ext-mbstring": "*", "ext-openssl": "*", "ext-curl": "*", @@ -3670,6 +3710,6 @@ }, "platform-dev": [], "platform-overrides": { - "php": "5.5.9" + "php": "5.6.4" } } diff --git a/index.php b/index.php index 0082125034..db077f5eb6 100644 --- a/index.php +++ b/index.php @@ -7,7 +7,7 @@ */ namespace Grav; -define('GRAV_PHP_MIN', '5.5.9'); +define('GRAV_PHP_MIN', '5.6.4'); // Ensure vendor libraries exist $autoload = __DIR__ . '/vendor/autoload.php'; @@ -15,7 +15,7 @@ die("Please run: bin/grav install"); } -if (PHP_SAPI == 'cli-server') { +if (PHP_SAPI === 'cli-server') { if (!isset($_SERVER['PHP_CLI_ROUTER'])) { die("PHP webserver requires a router to run Grav, please use:
php -S {$_SERVER['SERVER_NAME']}:{$_SERVER['SERVER_PORT']} system/router.php
"); } diff --git a/system/blueprints/config/system.yaml b/system/blueprints/config/system.yaml index 53f8ba179e..5bc2e49706 100644 --- a/system/blueprints/config/system.yaml +++ b/system/blueprints/config/system.yaml @@ -996,6 +996,18 @@ form: validate: type: bool + session.initialize: + type: toggle + label: PLUGIN_ADMIN.SESSION_INITIALIZE + help: PLUGIN_ADMIN.SESSION_INITIALIZE_HELP + highlight: 1 + options: + 1: PLUGIN_ADMIN.YES + 0: PLUGIN_ADMIN.NO + default: true + validate: + type: bool + session.timeout: type: text size: small @@ -1206,3 +1218,27 @@ form: placeholder: "e.g. http://yoursite.com/yourpath" label: PLUGIN_ADMIN.CUSTOM_BASE_URL help: PLUGIN_ADMIN.CUSTOM_BASE_URL_HELP + + strict_mode.yaml_compat: + type: toggle + label: PLUGIN_ADMIN.STRICT_YAML_COMPAT + highlight: 1 + default: 1 + help: PLUGIN_ADMIN.STRICT_YAML_COMPAT_HELP + options: + 1: PLUGIN_ADMIN.YES + 0: PLUGIN_ADMIN.NO + validate: + type: bool + + strict_mode.twig_compat: + type: toggle + label: PLUGIN_ADMIN.STRICT_TWIG_COMPAT + highlight: 1 + default: 1 + help: PLUGIN_ADMIN.STRICT_TWIG_COMPAT_HELP + options: + 1: PLUGIN_ADMIN.YES + 0: PLUGIN_ADMIN.NO + validate: + type: bool diff --git a/system/blueprints/user/account.yaml b/system/blueprints/user/account.yaml index 7717a01bb8..ac7717b3e3 100644 --- a/system/blueprints/user/account.yaml +++ b/system/blueprints/user/account.yaml @@ -94,6 +94,7 @@ form: twofa_secret: type: 2fa_secret outerclasses: 'twofa-secret' + markdown: true label: PLUGIN_ADMIN.2FA_SECRET sublabel: PLUGIN_ADMIN.2FA_SECRET_HELP diff --git a/system/config/system.yaml b/system/config/system.yaml index c05ee4abf7..065e059f20 100644 --- a/system/config/system.yaml +++ b/system/config/system.yaml @@ -88,7 +88,7 @@ twig: cache: true # Set to true to enable Twig caching debug: true # Enable Twig debug auto_reload: true # Refresh cache on changes - autoescape: false # Autoescape Twig vars + autoescape: false # Autoescape Twig vars (DEPRECATED, always enabled in strict mode) undefined_functions: true # Allow undefined functions undefined_filters: true # Allow undefined filters umask_fix: false # By default Twig creates cached files as 755, fix switches this to 775 @@ -146,3 +146,7 @@ gpm: method: 'auto' # Either 'curl', 'fopen' or 'auto'. 'auto' will try fopen first and if not available cURL verify_peer: true # Sometimes on some systems (Windows most commonly) GPM is unable to connect because the SSL certificate cannot be verified. Disabling this setting might help. official_gpm_only: true # By default GPM direct-install will only allow URLs via the official GPM proxy to ensure security + +strict_mode: + yaml_compat: true # Grav 1.5+: Enables YAML backwards compatibility + twig_compat: true # Grav 1.5+: Enables deprecated Twig autoescape setting (autoescape: false) diff --git a/system/defines.php b/system/defines.php index 28c4434f09..fe02c9e72c 100644 --- a/system/defines.php +++ b/system/defines.php @@ -8,12 +8,12 @@ // Some standard defines define('GRAV', true); -define('GRAV_VERSION', '1.4.6'); +define('GRAV_VERSION', '1.5.0'); define('GRAV_TESTING', false); define('DS', '/'); if (!defined('GRAV_PHP_MIN')) { - define('GRAV_PHP_MIN', '5.5.9'); + define('GRAV_PHP_MIN', '5.6.4'); } // Directories and Paths diff --git a/system/src/Grav/Common/Config/ConfigFileFinder.php b/system/src/Grav/Common/Config/ConfigFileFinder.php index 7980671754..83b13ec350 100644 --- a/system/src/Grav/Common/Config/ConfigFileFinder.php +++ b/system/src/Grav/Common/Config/ConfigFileFinder.php @@ -207,7 +207,7 @@ protected function detectInFolder($folder, $lookup = null) continue; } - $name = $directory->getBasename(); + $name = $directory->getFilename(); $find = ($lookup ?: $name) . '.yaml'; $filename = "{$path}/{$name}/{$find}"; diff --git a/system/src/Grav/Common/Config/Setup.php b/system/src/Grav/Common/Config/Setup.php index e9180fea8e..fba7fd1031 100644 --- a/system/src/Grav/Common/Config/Setup.php +++ b/system/src/Grav/Common/Config/Setup.php @@ -262,18 +262,22 @@ protected function check(UniformResourceLocator $locator) ); } - 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', ['config' => []]); - $this->initializeLocator($locator); - } + try { + 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', ['config' => []]); + $this->initializeLocator($locator); + } - // 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(); + // 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(); + } + } catch (\RuntimeException $e) { + throw new \RuntimeException(sprintf('Grav failed to initialize: %s', $e->getMessage()), 500, $e); } } } diff --git a/system/src/Grav/Common/Data/Validation.php b/system/src/Grav/Common/Data/Validation.php index 8ab5cf1854..3d982b7498 100644 --- a/system/src/Grav/Common/Data/Validation.php +++ b/system/src/Grav/Common/Data/Validation.php @@ -11,8 +11,8 @@ use Grav\Common\Grav; use Grav\Common\Utils; use Symfony\Component\Yaml\Exception\ParseException; -use Symfony\Component\Yaml\Parser; use Symfony\Component\Yaml\Yaml; +use RocketTheme\Toolbox\Compat\Yaml\Yaml as FallbackYaml; class Validation { @@ -107,7 +107,7 @@ public static function filter($value, array $field) $method = 'filter' . ucfirst(strtr($type, '-', '_')); // If this is a YAML field validate/filter as such - if ($type != 'yaml' && isset($field['yaml']) && $field['yaml'] === true) { + if ($type !== 'yaml' && isset($field['yaml']) && $field['yaml'] === true) { $method = 'filterYaml'; } @@ -128,10 +128,12 @@ public static function filter($value, array $field) */ public static function typeText($value, array $params, array $field) { - if (!is_string($value)) { + if (!is_string($value) && !is_numeric($value)) { return false; } + $value = (string)$value; + if (isset($params['min']) && strlen($value) < $params['min']) { return false; } @@ -643,13 +645,21 @@ protected static function filterList($value, array $params, array $field) public static function filterYaml($value, $params) { + if (!is_string($value)) { + return $value; + } + try { - if (is_string($value)) { - return (array) Yaml::parse($value); - } else { - return $value; - } + return (array) Yaml::parse($value); } catch (ParseException $e) { + // If YAML compatibility mode is set on, fall back to older YAML parser. + if (Grav::instance()['config']->get('system.strict_mode.yaml_compat', true)) { + try { + return (array) FallbackYaml::parse($value); + } catch (ParseException $e2) { + } + } + return $value; } } diff --git a/system/src/Grav/Common/Debugger.php b/system/src/Grav/Common/Debugger.php index 4687325e12..d687280340 100644 --- a/system/src/Grav/Common/Debugger.php +++ b/system/src/Grav/Common/Debugger.php @@ -58,8 +58,15 @@ public function init() $this->enabled = $this->config->get('system.debugger.enabled'); if ($this->enabled()) { + + $plugins_config = (array)$this->config->get('plugins'); + + ksort($plugins_config); + + $this->debugbar->addCollector(new ConfigCollector((array)$this->config->get('system'), 'Config')); - $this->debugbar->addCollector(new ConfigCollector((array)$this->config->get('plugins'), 'Plugins')); + $this->debugbar->addCollector(new ConfigCollector($plugins_config, 'Plugins')); + $this->addMessage('Grav v' . GRAV_VERSION); } return $this; diff --git a/system/src/Grav/Common/File/CompiledFile.php b/system/src/Grav/Common/File/CompiledFile.php index a0e230f143..4da55d9eb1 100644 --- a/system/src/Grav/Common/File/CompiledFile.php +++ b/system/src/Grav/Common/File/CompiledFile.php @@ -20,9 +20,6 @@ trait CompiledFile */ public function content($var = null) { - // Set some options - $this->settings(['native' => true, 'compat' => true]); - try { // If nothing has been loaded, attempt to get pre-compiled version of the file first. if ($var === null && $this->raw === null && $this->content === null) { diff --git a/system/src/Grav/Common/GPM/GPM.php b/system/src/Grav/Common/GPM/GPM.php index a7cbee402b..696ba657cd 100644 --- a/system/src/Grav/Common/GPM/GPM.php +++ b/system/src/Grav/Common/GPM/GPM.php @@ -13,7 +13,7 @@ use Grav\Common\Inflector; use Grav\Common\Iterator; use Grav\Common\Utils; -use Symfony\Component\Yaml\Yaml; +use RocketTheme\Toolbox\File\YamlFile; class GPM extends Iterator { @@ -624,7 +624,10 @@ public static function getBlueprints($source) return false; } - $blueprint = (array)Yaml::parse(file_get_contents($blueprint_file)); + $file = YamlFile::instance($blueprint_file); + $blueprint = (array)$file->content(); + $file->free(); + return $blueprint; } @@ -873,7 +876,9 @@ public function getDependencies($packages) // get currently installed version $locator = Grav::instance()['locator']; $blueprints_path = $locator->findResource('plugins://' . $dependency_slug . DS . 'blueprints.yaml'); - $package_yaml = Yaml::parse(file_get_contents($blueprints_path)); + $file = YamlFile::instance($blueprints_path); + $package_yaml = $file->content(); + $file->free(); $currentlyInstalledVersion = $package_yaml['version']; // if requirement is next significant release, check is compatible with currently installed version, might not be diff --git a/system/src/Grav/Common/GPM/Installer.php b/system/src/Grav/Common/GPM/Installer.php index 6c385b8d82..f16cdcca84 100644 --- a/system/src/Grav/Common/GPM/Installer.php +++ b/system/src/Grav/Common/GPM/Installer.php @@ -296,17 +296,17 @@ public static function sophisticatedInstall($source_path, $install_path, $ignore { foreach (new \DirectoryIterator($source_path) as $file) { - if ($file->isLink() || $file->isDot() || in_array($file->getBasename(),$ignores)) { + if ($file->isLink() || $file->isDot() || in_array($file->getFilename(), $ignores)) { continue; } - $path = $install_path . DS . $file->getBasename(); + $path = $install_path . DS . $file->getFilename(); if ($file->isDir()) { Folder::delete($path); Folder::move($file->getPathname(), $path); - if ($file->getBasename() == 'bin') { + if ($file->getFilename() === 'bin') { foreach (glob($path . DS . '*') as $bin_file) { @chmod($bin_file, 0755); } diff --git a/system/src/Grav/Common/GPM/Licenses.php b/system/src/Grav/Common/GPM/Licenses.php index fed5b642eb..6ca596a031 100644 --- a/system/src/Grav/Common/GPM/Licenses.php +++ b/system/src/Grav/Common/GPM/Licenses.php @@ -114,7 +114,7 @@ public static function getLicenseFile() { if (!isset(self::$file)) { - $path = Grav::instance()['locator']->findResource('user://data') . '/licenses.yaml';; + $path = Grav::instance()['locator']->findResource('user://data') . '/licenses.yaml'; if (!file_exists($path)) { touch($path); } diff --git a/system/src/Grav/Common/Grav.php b/system/src/Grav/Common/Grav.php index e6a49a1656..84260d01c0 100644 --- a/system/src/Grav/Common/Grav.php +++ b/system/src/Grav/Common/Grav.php @@ -9,7 +9,6 @@ namespace Grav\Common; use Grav\Common\Config\Config; -use Grav\Common\Language\Language; use Grav\Common\Page\Medium\ImageMedium; use Grav\Common\Page\Medium\Medium; use Grav\Common\Page\Page; @@ -205,11 +204,8 @@ public function redirect($route, $code = null) */ public function redirectLangSafe($route, $code = null) { - /** @var Language $language */ - $language = $this['language']; - - if (!$this['uri']->isExternal($route) && $language->enabled() && $language->isIncludeDefaultLanguage()) { - $this->redirect($language->getLanguage() . $route, $code); + if (!$this['uri']->isExternal($route)) { + $this->redirect($this['pages']->route($route), $code); } else { $this->redirect($route, $code); } @@ -443,7 +439,7 @@ public function fallbackUrl($path) /** @var Config $config */ $config = $this['config']; - $uri_extension = $uri->extension(); + $uri_extension = strtolower($uri->extension()); $fallback_types = $config->get('system.media.allowed_fallback_types', null); $supported_types = $config->get('media.types'); diff --git a/system/src/Grav/Common/Inflector.php b/system/src/Grav/Common/Inflector.php index f4a17ba7c5..61c2338095 100644 --- a/system/src/Grav/Common/Inflector.php +++ b/system/src/Grav/Common/Inflector.php @@ -190,10 +190,11 @@ public function underscorize($word) public function hyphenize($word) { $regex1 = preg_replace('/([A-Z]+)([A-Z][a-z])/', '\1-\2', $word); - $regex2 = preg_replace('/([a-zd])([A-Z])/', '\1-\2', $regex1); - $regex3 = preg_replace('/[^A-Z^a-z^0-9]+/', '-', $regex2); + $regex2 = preg_replace('/([a-z])([A-Z])/', '\1-\2', $regex1); + $regex3 = preg_replace('/([0-9])([A-Z])/', '\1-\2', $regex2); + $regex4 = preg_replace('/[^A-Z^a-z^0-9]+/', '-', $regex3); - return strtolower($regex3); + return strtolower($regex4); } /** diff --git a/system/src/Grav/Common/Media/Interfaces/MediaCollectionInterface.php b/system/src/Grav/Common/Media/Interfaces/MediaCollectionInterface.php new file mode 100644 index 0000000000..06f71a5ece --- /dev/null +++ b/system/src/Grav/Common/Media/Interfaces/MediaCollectionInterface.php @@ -0,0 +1,9 @@ +getMediaFolder(); + + if (strpos($folder, '://')) { + return $folder; + } + + /** @var UniformResourceLocator $locator */ + $locator = Grav::instance()['locator']; + $user = $locator->findResource('user://'); + if (strpos($folder, $user) === 0) { + return 'user://' . substr($folder, strlen($user)+1); + } + + return null; + } + + /** + * Gets the associated media collection. + * + * @return MediaCollectionInterface Representation of associated media. + */ + public function getMedia() + { + $cache = $this->getMediaCache(); + + if ($this->media === null) { + // Use cached media if possible. + $cacheKey = md5('media' . $this->getCacheKey()); + if (!$media = $cache->fetch($cacheKey)) { + $media = new Media($this->getMediaFolder(), $this->getMediaOrder()); + $cache->save($cacheKey, $media); + } + $this->media = $media; + } + + return $this->media; + } + + /** + * Sets the associated media collection. + * + * @param MediaCollectionInterface $media Representation of associated media. + * @return $this + */ + protected function setMedia(MediaCollectionInterface $media) + { + $cache = $this->getMediaCache(); + $cacheKey = md5('media' . $this->getCacheKey()); + $cache->save($cacheKey, $media); + + $this->media = $media; + + return $this; + } + + /** + * Clear media cache. + */ + protected function clearMediaCache() + { + $cache = $this->getMediaCache(); + $cacheKey = md5('media' . $this->getCacheKey()); + $cache->delete($cacheKey); + } + + /** + * @return Cache + */ + protected function getMediaCache() + { + return Grav::instance()['cache']; + } + + /** + * @return string + */ + abstract protected function getCacheKey(); +} diff --git a/system/src/Grav/Common/Page/Interfaces/PageInterface.php b/system/src/Grav/Common/Page/Interfaces/PageInterface.php new file mode 100644 index 0000000000..14d6cdc1e5 --- /dev/null +++ b/system/src/Grav/Common/Page/Interfaces/PageInterface.php @@ -0,0 +1,9 @@ +path = $path; + $this->media_order = $media_order; $this->__wakeup(); $this->init(); @@ -86,7 +88,7 @@ protected function init() /** @var \DirectoryIterator $info */ foreach ($iterator as $path => $info) { // Ignore folders and Markdown files. - if (!$info->isFile() || $info->getExtension() === 'md' || $info->getBasename()[0] === '.') { + if (!$info->isFile() || $info->getExtension() === 'md' || $info->getFilename()[0] === '.') { continue; } diff --git a/system/src/Grav/Common/Page/Medium/AbstractMedia.php b/system/src/Grav/Common/Page/Medium/AbstractMedia.php index 4c95938c30..58f9886101 100644 --- a/system/src/Grav/Common/Page/Medium/AbstractMedia.php +++ b/system/src/Grav/Common/Page/Medium/AbstractMedia.php @@ -10,9 +10,11 @@ use Grav\Common\Getters; use Grav\Common\Grav; +use Grav\Common\Media\Interfaces\MediaCollectionInterface; +use Grav\Common\Media\Interfaces\MediaObjectInterface; use Grav\Common\Utils; -abstract class AbstractMedia extends Getters +abstract class AbstractMedia extends Getters implements MediaCollectionInterface { protected $gettersVariable = 'instances'; @@ -21,6 +23,7 @@ abstract class AbstractMedia extends Getters protected $videos = []; protected $audios = []; protected $files = []; + protected $media_order; /** * Get medium by filename. @@ -62,7 +65,7 @@ public function offsetGet($offset) /** * Get a list of all media. * - * @return array|Medium[] + * @return array|MediaObjectInterface[] */ public function all() { @@ -74,7 +77,7 @@ public function all() /** * Get a list of all image media. * - * @return array|Medium[] + * @return array|MediaObjectInterface[] */ public function images() { @@ -85,7 +88,7 @@ public function images() /** * Get a list of all video media. * - * @return array|Medium[] + * @return array|MediaObjectInterface[] */ public function videos() { @@ -96,7 +99,7 @@ public function videos() /** * Get a list of all audio media. * - * @return array|Medium[] + * @return array|MediaObjectInterface[] */ public function audios() { @@ -107,7 +110,7 @@ public function audios() /** * Get a list of all file media. * - * @return array|Medium[] + * @return array|MediaObjectInterface[] */ public function files() { @@ -117,7 +120,7 @@ public function files() /** * @param string $name - * @param Medium $file + * @param MediaObjectInterface $file */ protected function add($name, $file) { @@ -145,14 +148,20 @@ protected function add($name, $file) */ protected function orderMedia($media) { - $page = Grav::instance()['pages']->get($this->path); + if (null === $this->media_order) { + $page = Grav::instance()['pages']->get($this->path); - if ($page && isset($page->header()->media_order)) { - $media_order = array_map('trim', explode(',', $page->header()->media_order)); - $media = Utils::sortArrayByArray($media, $media_order); + if ($page && isset($page->header()->media_order)) { + $this->media_order = array_map('trim', explode(',', $page->header()->media_order)); + } + } + + if (!empty($this->media_order) && is_array($this->media_order)) { + $media = Utils::sortArrayByArray($media, $this->media_order); } else { ksort($media, SORT_NATURAL | SORT_FLAG_CASE); } + return $media; } diff --git a/system/src/Grav/Common/Page/Medium/ImageMedium.php b/system/src/Grav/Common/Page/Medium/ImageMedium.php index 2851225dd1..2f1f176a62 100644 --- a/system/src/Grav/Common/Page/Medium/ImageMedium.php +++ b/system/src/Grav/Common/Page/Medium/ImageMedium.php @@ -11,6 +11,7 @@ use Grav\Common\Data\Blueprint; use Grav\Common\Grav; use Grav\Common\Utils; +use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator; class ImageMedium extends Medium { @@ -164,12 +165,18 @@ public function path($reset = true) */ public function url($reset = true) { - $image_path = Grav::instance()['locator']->findResource('cache://images', true); - $image_dir = Grav::instance()['locator']->findResource('cache://images', false); + /** @var UniformResourceLocator $locator */ + $locator = Grav::instance()['locator']; + $image_path = $locator->findResource('cache://images', true); + $image_dir = $locator->findResource('cache://images', false); $saved_image_path = $this->saveImage(); $output = preg_replace('|^' . preg_quote(GRAV_ROOT, '|') . '|', '', $saved_image_path); + if ($locator->isStream($output)) { + $output = $locator->findResource($output, false); + } + if (Utils::startsWith($output, $image_path)) { $output = '/' . $image_dir . preg_replace('|^' . preg_quote($image_path, '|') . '|', '', $output); } diff --git a/system/src/Grav/Common/Page/Medium/Medium.php b/system/src/Grav/Common/Page/Medium/Medium.php index 36b893c4bd..0a4b9bf5f8 100644 --- a/system/src/Grav/Common/Page/Medium/Medium.php +++ b/system/src/Grav/Common/Page/Medium/Medium.php @@ -12,9 +12,9 @@ use Grav\Common\Grav; use Grav\Common\Data\Data; use Grav\Common\Data\Blueprint; -use Grav\Common\Utils; +use Grav\Common\Media\Interfaces\MediaObjectInterface; -class Medium extends Data implements RenderableInterface +class Medium extends Data implements RenderableInterface, MediaObjectInterface { use ParsedownHtmlTrait; @@ -199,7 +199,12 @@ public function relativePath($reset = true) */ public function url($reset = true) { - $output = preg_replace('|^' . preg_quote(GRAV_ROOT) . '|', '', $this->get('filepath')); + $output = preg_replace('|^' . preg_quote(GRAV_ROOT, '|') . '|', '', $this->get('filepath')); + + $locator = Grav::instance()['locator']; + if ($locator->isStream($output)) { + $output = $locator->findResource($output, false); + } if ($reset) { $this->reset(); diff --git a/system/src/Grav/Common/Page/Medium/VideoMedium.php b/system/src/Grav/Common/Page/Medium/VideoMedium.php index b3c8eed852..23271bf35d 100644 --- a/system/src/Grav/Common/Page/Medium/VideoMedium.php +++ b/system/src/Grav/Common/Page/Medium/VideoMedium.php @@ -94,6 +94,40 @@ public function autoplay($status = false) return $this; } + /** + * Allows to set the playsinline attribute + * + * @param bool $status + * @return $this + */ + public function playsinline($status = false) + { + if($status) { + $this->attributes['playsinline'] = true; + } else { + unset($this->attributes['playsinline']); + } + + return $this; + } + + /** + * Allows to set the muted attribute + * + * @param bool $status + * @return $this + */ + public function muted($status = false) + { + if($status) { + $this->attributes['muted'] = true; + } else { + unset($this->attributes['muted']); + } + + return $this; + } + /** * Reset medium. * diff --git a/system/src/Grav/Common/Page/Page.php b/system/src/Grav/Common/Page/Page.php index e92e03bffb..b1c8b9775f 100644 --- a/system/src/Grav/Common/Page/Page.php +++ b/system/src/Grav/Common/Page/Page.php @@ -12,11 +12,14 @@ use Grav\Common\Cache; use Grav\Common\Config\Config; use Grav\Common\Data\Blueprint; +use Grav\Common\File\CompiledYamlFile; use Grav\Common\Filesystem\Folder; use Grav\Common\Grav; use Grav\Common\Language\Language; use Grav\Common\Markdown\Parsedown; use Grav\Common\Markdown\ParsedownExtra; +use Grav\Common\Page\Interfaces\PageInterface; +use Grav\Common\Media\Traits\MediaTrait; use Grav\Common\Taxonomy; use Grav\Common\Uri; use Grav\Common\Utils; @@ -27,8 +30,10 @@ define('PAGE_ORDER_PREFIX_REGEX', '/^[0-9]+\./u'); -class Page +class Page implements PageInterface { + use MediaTrait; + /** * @var string Filename. Leave as null if page is folder. */ @@ -65,7 +70,6 @@ class Page protected $summary; protected $raw_content; protected $pagination; - protected $media; protected $metadata; protected $title; protected $max_count; @@ -318,8 +322,6 @@ public function header($var = null) if (!$this->header) { $file = $this->file(); if ($file) { - // Set some options - $file->settings(['native' => true, 'compat' => true]); try { $this->raw_content = $file->markdown(); $this->frontmatter = $file->frontmatter(); @@ -328,11 +330,12 @@ public function header($var = null) if (!Utils::isAdminPlugin()) { // If there's a `frontmatter.yaml` file merge that in with the page header // note page's own frontmatter has precedence and will overwrite any defaults - $frontmatter_file = $this->path . '/' . $this->folder . '/frontmatter.yaml'; - if (file_exists($frontmatter_file)) { - $frontmatter_data = (array)Yaml::parse(file_get_contents($frontmatter_file)); + $frontmatterFile = CompiledYamlFile::instance($this->path . '/' . $this->folder . '/frontmatter.yaml'); + if ($frontmatterFile->exists()) { + $frontmatter_data = (array)$frontmatterFile->content(); $this->header = (object)array_replace_recursive($frontmatter_data, (array)$this->header); + $frontmatterFile->free(); } // Process frontmatter with Twig if enabled if (Grav::instance()['config']->get('system.pages.frontmatter.process_twig') === true) { @@ -813,6 +816,8 @@ public function getRawContent() */ public function setRawContent($content) { + $content = $content === null ? '': $content; + $this->content = $content; } @@ -1122,6 +1127,14 @@ public function toJson() return json_encode($this->toArray()); } + /** + * @return string + */ + protected function getCacheKey() + { + return $this->id(); + } + /** * Gets and sets the associated media as found in the page folder. * @@ -1131,23 +1144,33 @@ public function toJson() */ public function media($var = null) { - /** @var Cache $cache */ - $cache = Grav::instance()['cache']; - if ($var) { - $this->media = $var; - } - if ($this->media === null) { - // Use cached media if possible. - $media_cache_id = md5('media' . $this->id()); - if (!$media = $cache->fetch($media_cache_id)) { - $media = new Media($this->path()); - $cache->save($media_cache_id, $media); - } - $this->media = $media; + $this->setMedia($var); } - return $this->media; + return $this->getMedia(); + } + + /** + * Get filesystem path to the associated media. + * + * @return string|null + */ + public function getMediaFolder() + { + return $this->path(); + } + + /** + * Get display order for the associated media. + * + * @return array Empty array means default ordering. + */ + public function getMediaOrder() + { + $header = $this->header(); + + return isset($header->media_order) ? array_map('trim', explode(',', (string)$header->media_order)) : []; } /** @@ -1626,14 +1649,19 @@ public function canonical($include_lang = true) * Gets the url for the Page. * * @param bool $include_host Defaults false, but true would include http://yourhost.com - * @param bool $canonical true to return the canonical URL - * @param bool $include_lang + * @param bool $canonical True to return the canonical URL + * @param bool $include_base Include base url on multisite as well as language code * @param bool $raw_route * * @return string The url. */ - public function url($include_host = false, $canonical = false, $include_lang = true, $raw_route = false) + public function url($include_host = false, $canonical = false, $include_base = true, $raw_route = false) { + // Override any URL when external_url is set + if (isset($this->external_url)) { + return $this->external_url; + } + $grav = Grav::instance(); /** @var Pages $pages */ @@ -1642,41 +1670,25 @@ public function url($include_host = false, $canonical = false, $include_lang = t /** @var Config $config */ $config = $grav['config']; - /** @var Language $language */ - $language = $grav['language']; - - /** @var Uri $uri */ - $uri = $grav['uri']; - - // Override any URL when external_url is set - if (isset($this->external_url)) { - return $this->external_url; - } - - // get pre-route - if ($include_lang && $language->enabled()) { - $pre_route = $language->getLanguageURLPrefix(); - } else { - $pre_route = ''; - } + // get base route (multisite base and language) + $route = $include_base ? $pages->baseRoute() : ''; // add full route if configured to do so - if ($config->get('system.absolute_urls', false)) { + if (!$include_host && $config->get('system.absolute_urls', false)) { $include_host = true; } - // get canonical route if requested if ($canonical) { - $route = $pre_route . $this->routeCanonical(); + $route .= $this->routeCanonical(); } elseif ($raw_route) { - $route = $pre_route . $this->rawRoute(); + $route .= $this->rawRoute(); } else { - $route = $pre_route . $this->route(); + $route .= $this->route(); } - $rootUrl = $uri->rootUrl($include_host) . $pages->base(); - - $url = $rootUrl . '/' . trim($route, '/') . $this->urlExtension(); + /** @var Uri $uri */ + $uri = $grav['uri']; + $url = $uri->rootUrl($include_host) . '/' . trim($route, '/') . $this->urlExtension(); // trim trailing / if not root if ($url !== '/') { @@ -1790,7 +1802,7 @@ public function routeAliases($var = null) public function routeCanonical($var = null) { if ($var !== null) { - $this->routes['canonical'] = (array)$var; + $this->routes['canonical'] = $var; } if (!empty($this->routes) && isset($this->routes['canonical'])) { diff --git a/system/src/Grav/Common/Page/Pages.php b/system/src/Grav/Common/Page/Pages.php index 12afa7e88d..ac0e41cb02 100644 --- a/system/src/Grav/Common/Page/Pages.php +++ b/system/src/Grav/Common/Page/Pages.php @@ -49,7 +49,7 @@ class Pages /** * @var array|string[] */ - protected $baseUrl = []; + protected $baseRoute = []; /** * @var array|string[] @@ -120,7 +120,7 @@ public function base($path = null) if ($path !== null) { $path = trim($path, '/'); $this->base = $path ? '/' . $path : null; - $this->baseUrl = []; + $this->baseRoute = []; } return $this->base; @@ -128,39 +128,61 @@ public function base($path = null) /** * - * Get base URL for Grav pages. + * Get base route for Grav pages. * - * @param string $lang Optional language code for multilingual links. - * @param bool $absolute If true, return absolute url, if false, return relative url. Otherwise return default. + * @param string $lang Optional language code for multilingual routes. * * @return string */ - public function baseUrl($lang = null, $absolute = null) + public function baseRoute($lang = null) { - $lang = (string) $lang; - $type = $absolute === null ? 'base_url' : ($absolute ? 'base_url_absolute' : 'base_url_relative'); - $key = "{$lang} {$type}"; - - if (!isset($this->baseUrl[$key])) { - /** @var Config $config */ - $config = $this->grav['config']; + $key = $lang ?: 'default'; + if (!isset($this->baseRoute[$key])) { /** @var Language $language */ $language = $this->grav['language']; - if (!$lang) { - $lang = $language->getActive(); - } + $path_base = rtrim($this->base(), '/'); + $path_lang = $language->enabled() ? $language->getLanguageURLPrefix($lang) : ''; - $path_append = rtrim($this->grav['pages']->base(), '/'); - if ($language->getDefault() !== $lang || $config->get('system.languages.include_default_lang') === true) { - $path_append .= $lang ? '/' . $lang : ''; - } + $this->baseRoute[$key] = $path_base . $path_lang; + } + + return $this->baseRoute[$key]; + } - $this->baseUrl[$key] = $this->grav[$type] . $path_append; + /** + * + * Get route for Grav site. + * + * @param string $route Optional route to the page. + * @param string $lang Optional language code for multilingual links. + * + * @return string + */ + public function route($route = '/', $lang = null) + { + if (!$route || $route === '/') { + return $this->baseRoute($lang) ?: '/'; } - return $this->baseUrl[$key]; + return $this->baseRoute($lang) . $route; + } + + /** + * + * Get base URL for Grav pages. + * + * @param string $lang Optional language code for multilingual links. + * @param bool|null $absolute If true, return absolute url, if false, return relative url. Otherwise return default. + * + * @return string + */ + public function baseUrl($lang = null, $absolute = null) + { + $type = $absolute === null ? 'base_url' : ($absolute ? 'base_url_absolute' : 'base_url_relative'); + + return $this->grav[$type] . $this->baseRoute($lang); } /** @@ -179,7 +201,7 @@ public function homeUrl($lang = null, $absolute = null) /** * - * Get home URL for Grav site. + * Get URL for Grav site. * * @param string $route Optional route to the page. * @param string $lang Optional language code for multilingual links. @@ -189,7 +211,7 @@ public function homeUrl($lang = null, $absolute = null) */ public function url($route = '/', $lang = null, $absolute = null) { - if ($route === '/') { + if (!$route || $route === '/') { return $this->homeUrl($lang, $absolute); } @@ -1041,7 +1063,7 @@ function ($str) { } // Ignore all files in ignore list. - if (\in_array($file->getBasename(), $this->ignore_files, true)) { + if (\in_array($filename, $this->ignore_files, true)) { continue; } diff --git a/system/src/Grav/Common/Plugins.php b/system/src/Grav/Common/Plugins.php index 5e407a905d..2a9d523811 100644 --- a/system/src/Grav/Common/Plugins.php +++ b/system/src/Grav/Common/Plugins.php @@ -33,7 +33,7 @@ public function __construct() if (!$directory->isDir()) { continue; } - $plugins[] = $directory->getBasename(); + $plugins[] = $directory->getFilename(); } natsort($plugins); diff --git a/system/src/Grav/Common/Processors/InitializeProcessor.php b/system/src/Grav/Common/Processors/InitializeProcessor.php index e3f25691c9..96e6a7e805 100644 --- a/system/src/Grav/Common/Processors/InitializeProcessor.php +++ b/system/src/Grav/Common/Processors/InitializeProcessor.php @@ -8,6 +8,10 @@ namespace Grav\Common\Processors; +use Grav\Common\Config\Config; +use Grav\Common\Uri; +use Grav\Common\Utils; + class InitializeProcessor extends ProcessorBase implements ProcessorInterface { public $id = 'init'; @@ -15,29 +19,36 @@ class InitializeProcessor extends ProcessorBase implements ProcessorInterface public function process() { - $this->container['config']->debug(); + /** @var Config $config */ + $config = $this->container['config']; + $config->debug(); // Use output buffering to prevent headers from being sent too early. ob_start(); - if ($this->container['config']->get('system.cache.gzip')) { + if ($config->get('system.cache.gzip') && !@ob_start('ob_gzhandler')) { // Enable zip/deflate with a fallback in case of if browser does not support compressing. - if (!@ob_start("ob_gzhandler")) { - ob_start(); - } + ob_start(); } // Initialize the timezone. - if ($this->container['config']->get('system.timezone')) { + if ($config->get('system.timezone')) { date_default_timezone_set($this->container['config']->get('system.timezone')); } // FIXME: Initialize session should happen later after plugins have been loaded. This is a workaround to fix session issues in AWS. - if ($this->container['config']->get('system.session.initialize', 1) && isset($this->container['session'])) { + if (isset($this->container['session']) && $config->get('system.session.initialize', true)) { $this->container['session']->init(); } - // Initialize uri. - $this->container['uri']->init(); + /** @var Uri $uri */ + $uri = $this->container['uri']; + $uri->init(); + + // Redirect pages with trailing slash if configured to do so. + $path = $uri->path() ?: '/'; + if ($path !== '/' && $config->get('system.pages.redirect_trailing_slash', false) && Utils::endsWith($path, '/')) { + $this->container->redirect(rtrim($path, '/'), 302); + } $this->container->setLocale(); } diff --git a/system/src/Grav/Common/Service/ConfigServiceProvider.php b/system/src/Grav/Common/Service/ConfigServiceProvider.php index 25c1a126ee..379c042e72 100644 --- a/system/src/Grav/Common/Service/ConfigServiceProvider.php +++ b/system/src/Grav/Common/Service/ConfigServiceProvider.php @@ -16,6 +16,7 @@ use Grav\Common\Config\Setup; use Pimple\Container; use Pimple\ServiceProviderInterface; +use RocketTheme\Toolbox\File\YamlFile; use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator; class ConfigServiceProvider implements ServiceProviderInterface @@ -31,7 +32,14 @@ public function register(Container $container) }; $container['config'] = function ($c) { - return static::load($c); + $config = static::load($c); + + // After configuration has been loaded, we can disable YAML compatibility if strict mode has been enabled. + if (!$config->get('system.strict_mode.yaml_compat', true)) { + YamlFile::globalSettings(['compat' => false, 'native' => true]); + } + + return $config; }; $container['languages'] = function ($c) { @@ -65,6 +73,10 @@ public static function blueprints(Container $container) return $blueprints->name("master-{$setup->environment}")->load(); } + /** + * @param Container $container + * @return Config + */ public static function load(Container $container) { /** Setup $setup */ diff --git a/system/src/Grav/Common/Service/PageServiceProvider.php b/system/src/Grav/Common/Service/PageServiceProvider.php index bf55f791b4..ccfa58089a 100644 --- a/system/src/Grav/Common/Service/PageServiceProvider.php +++ b/system/src/Grav/Common/Service/PageServiceProvider.php @@ -8,6 +8,7 @@ namespace Grav\Common\Service; +use Grav\Common\Config\Config; use Grav\Common\Grav; use Grav\Common\Language\Language; use Grav\Common\Page\Page; @@ -26,35 +27,33 @@ public function register(Container $container) /** @var Pages $pages */ $pages = $c['pages']; + /** @var Config $config */ + $config = $c['config']; + /** @var Uri $uri */ $uri = $c['uri']; - $path = $uri->path(); // Don't trim to support trailing slash default routes - $path = $path ?: '/'; - + $path = $uri->path() ?: '/'; // Don't trim to support trailing slash default routes $page = $pages->dispatch($path); // Redirection tests if ($page) { - /** @var Language $language */ - $language = $c['language']; - // some debugger override logic if ($page->debugger() === false) { $c['debugger']->enabled(false); } - if ($c['config']->get('system.force_ssl')) { - if (!isset($_SERVER['HTTPS']) || $_SERVER["HTTPS"] != "on") { - $url = "https://" . $_SERVER["HTTP_HOST"] . $_SERVER["REQUEST_URI"]; + if ($config->get('system.force_ssl')) { + if (!isset($_SERVER['HTTPS']) || $_SERVER['HTTPS'] !== 'on') { + $url = 'https://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']; $c->redirect($url); } } - $url = $page->route(); + $url = $pages->route($page->route()); if ($uri->params()) { - if ($url == '/') { //Avoid double slash + if ($url === '/') { //Avoid double slash $url = $uri->params(); } else { $url .= $uri->params(); @@ -67,18 +66,16 @@ public function register(Container $container) $url .= '#' . $uri->fragment(); } + /** @var Language $language */ + $language = $c['language']; + // Language-specific redirection scenarios - if ($language->enabled()) { - if ($language->isLanguageInUrl() && !$language->isIncludeDefaultLanguage()) { - $c->redirect($url); - } - if (!$language->isLanguageInUrl() && $language->isIncludeDefaultLanguage()) { - $c->redirectLangSafe($url); - } + if ($language->enabled() && ($language->isLanguageInUrl() xor $language->isIncludeDefaultLanguage())) { + $c->redirect($url); } // Default route test and redirect - if ($c['config']->get('system.pages.redirect_default_route') && $page->route() != $path) { - $c->redirectLangSafe($url); + if ($config->get('system.pages.redirect_default_route') && $page->route() !== $path) { + $c->redirect($url); } } diff --git a/system/src/Grav/Common/Service/SessionServiceProvider.php b/system/src/Grav/Common/Service/SessionServiceProvider.php index 52e107b7aa..9262efa829 100644 --- a/system/src/Grav/Common/Service/SessionServiceProvider.php +++ b/system/src/Grav/Common/Service/SessionServiceProvider.php @@ -29,21 +29,22 @@ public function register(Container $container) /** @var Uri $uri */ $uri = $c['uri']; - // Get session parameters. - $session_timeout = (int)$config->get('system.session.timeout', 1800); - $session_path = $config->get('system.session.path'); - if (null === $session_path) { - $session_path = '/' . ltrim(Uri::filterPath($uri->rootUrl(false)), '/'); - } - $domain = $uri->host(); - if ($domain === 'localhost') { - $domain = ''; - } - // Get session options. - $secure = (bool)$config->get('system.session.secure', false); - $httponly = (bool)$config->get('system.session.httponly', true); $enabled = (bool)$config->get('system.session.enabled', false); + $cookie_secure = (bool)$config->get('system.session.secure', false); + $cookie_httponly = (bool)$config->get('system.session.httponly', true); + $cookie_lifetime = (int)$config->get('system.session.timeout', 1800); + $cookie_path = $config->get('system.session.path'); + if (null === $cookie_path) { + $cookie_path = '/' . trim(Uri::filterPath($uri->rootUrl(false)), '/'); + } + // Session cookie path requires trailing slash. + $cookie_path = rtrim($cookie_path, '/') . '/'; + + $cookie_domain = $uri->host(); + if ($cookie_domain === 'localhost') { + $cookie_domain = ''; + } // Activate admin if we're inside the admin path. $is_admin = false; @@ -56,14 +57,14 @@ public function register(Container $container) // Check no language, simple language prefix (en) and region specific language prefix (en-US). $pos = strpos($current_route, $base); if ($pos === 0 || $pos === 3 || $pos === 6) { - $session_timeout = $config->get('plugins.admin.session.timeout', 1800); + $cookie_lifetime = $config->get('plugins.admin.session.timeout', 1800); $enabled = $is_admin = true; } } // Fix for HUGE session timeouts. - if ($session_timeout > 99999999999) { - $session_timeout = 9999999999; + if ($cookie_lifetime > 99999999999) { + $cookie_lifetime = 9999999999; } $inflector = new Inflector(); @@ -73,10 +74,16 @@ public function register(Container $container) } // Define session service. - $session = new Session($session_timeout, $session_path, $domain); - $session->setName($session_name); - $session->setSecure($secure); - $session->setHttpOnly($httponly); + $options = [ + 'name' => $session_name, + 'cookie_lifetime' => $cookie_lifetime, + 'cookie_path' => $cookie_path, + 'cookie_domain' => $cookie_domain, + 'cookie_secure' => $cookie_secure, + 'cookie_httponly' => $cookie_httponly + ] + (array) $config->get('system.session.options'); + + $session = new Session($options); $session->setAutoStart($enabled); return $session; diff --git a/system/src/Grav/Common/Session.php b/system/src/Grav/Common/Session.php index 7573f08660..f7221194fd 100644 --- a/system/src/Grav/Common/Session.php +++ b/system/src/Grav/Common/Session.php @@ -8,34 +8,18 @@ namespace Grav\Common; -use RocketTheme\Toolbox\Session\Session as BaseSession; - -class Session extends BaseSession +class Session extends \Grav\Framework\Session\Session { /** @var bool */ protected $autoStart = false; - protected $lifetime; - protected $path; - protected $domain; - protected $secure; - protected $httpOnly; - /** - * @param int $lifetime Defaults to 1800 seconds. - * @param string $path Cookie path. - * @param string $domain Optional, domain for the session - * @throws \RuntimeException + * @return \Grav\Framework\Session\Session + * @deprecated 1.5 */ - public function __construct($lifetime, $path, $domain = null) + public static function instance() { - $this->lifetime = $lifetime; - $this->path = $path; - $this->domain = $domain; - - if (php_sapi_name() !== 'cli') { - parent::__construct($lifetime, $path, $domain); - } + return static::getInstance(); } /** @@ -48,9 +32,6 @@ public function init() if ($this->autoStart) { $this->start(); - // TODO: This setcookie shouldn't be here, session should by itself be able to update its cookie. - setcookie(session_name(), session_id(), $this->lifetime ? time() + $this->lifetime : 0, $this->path, $this->domain, $this->secure, $this->httpOnly); - $this->autoStart = false; } } @@ -67,27 +48,25 @@ public function setAutoStart($auto) } /** - * @param bool $secure - * @return $this + * Returns attributes. + * + * @return array Attributes + * @deprecated 1.5 */ - public function setSecure($secure) + public function all() { - $this->secure = $secure; - ini_set('session.cookie_secure', (bool)$secure); - - return $this; + return $this->getAll(); } /** - * @param bool $httpOnly - * @return $this + * Checks if the session was started. + * + * @return Boolean + * @deprecated 1.5 */ - public function setHttpOnly($httpOnly) + public function started() { - $this->httpOnly = $httpOnly; - ini_set('session.cookie_httponly', (bool)$httpOnly); - - return $this; + return $this->isStarted(); } /** diff --git a/system/src/Grav/Common/Themes.php b/system/src/Grav/Common/Themes.php index 8f29037dd7..bf86d8dc66 100644 --- a/system/src/Grav/Common/Themes.php +++ b/system/src/Grav/Common/Themes.php @@ -98,7 +98,7 @@ public function all() continue; } - $theme = $directory->getBasename(); + $theme = $directory->getFilename(); $result = self::get($theme); if ($result) { diff --git a/system/src/Grav/Common/Twig/Node/TwigNodeMarkdown.php b/system/src/Grav/Common/Twig/Node/TwigNodeMarkdown.php index 5943c952a7..0aa12eae54 100644 --- a/system/src/Grav/Common/Twig/Node/TwigNodeMarkdown.php +++ b/system/src/Grav/Common/Twig/Node/TwigNodeMarkdown.php @@ -12,7 +12,7 @@ class TwigNodeMarkdown extends \Twig_Node implements \Twig_NodeOutputInterface { public function __construct(\Twig_Node $body, $lineno, $tag = 'markdown') { - parent::__construct(array('body' => $body), array(), $lineno, $tag); + parent::__construct(['body' => $body], [], $lineno, $tag); } /** * Compiles the node to PHP. diff --git a/system/src/Grav/Common/Twig/Node/TwigNodeSwitch.php b/system/src/Grav/Common/Twig/Node/TwigNodeSwitch.php index e0e8127006..1630e5eaef 100644 --- a/system/src/Grav/Common/Twig/Node/TwigNodeSwitch.php +++ b/system/src/Grav/Common/Twig/Node/TwigNodeSwitch.php @@ -24,20 +24,17 @@ public function compile(\Twig_Compiler $compiler) { $compiler ->addDebugInfo($this) - ->write("switch (") + ->write('switch (') ->subcompile($this->getNode('value')) ->raw(") {\n") ->indent(); - foreach ($this->getNode('cases') as $case) - { - if (!$case->hasNode('body')) - { + foreach ($this->getNode('cases') as $case) { + if (!$case->hasNode('body')) { continue; } - foreach ($case->getNode('values') as $value) - { + foreach ($case->getNode('values') as $value) { $compiler ->write('case ') ->subcompile($value) @@ -53,8 +50,7 @@ public function compile(\Twig_Compiler $compiler) ->write("}\n"); } - if ($this->hasNode('default') && $this->getNode('default') !== null) - { + if ($this->hasNode('default') && $this->getNode('default') !== null) { $compiler ->write("default:\n") ->write("{\n") diff --git a/system/src/Grav/Common/Twig/TokenParser/TwigTokenParserSwitch.php b/system/src/Grav/Common/Twig/TokenParser/TwigTokenParserSwitch.php index 2da932de4c..0768b3071e 100644 --- a/system/src/Grav/Common/Twig/TokenParser/TwigTokenParserSwitch.php +++ b/system/src/Grav/Common/Twig/TokenParser/TwigTokenParserSwitch.php @@ -37,8 +37,7 @@ public function parse(\Twig_Token $token) $stream->expect(\Twig_Token::BLOCK_END_TYPE); // There can be some whitespace between the {% switch %} and first {% case %} tag. - while ($stream->getCurrent()->getType() == \Twig_Token::TEXT_TYPE && trim($stream->getCurrent()->getValue()) == '') - { + while ($stream->getCurrent()->getType() === \Twig_Token::TEXT_TYPE && trim($stream->getCurrent()->getValue()) === '') { $stream->next(); } @@ -47,56 +46,45 @@ public function parse(\Twig_Token $token) $expressionParser = $this->parser->getExpressionParser(); $default = null; - $cases = array(); + $cases = []; $end = false; - while (!$end) - { + while (!$end) { $next = $stream->next(); - switch ($next->getValue()) - { + switch ($next->getValue()) { case 'case': - { - $values = array(); - - while (true) - { - $values[] = $expressionParser->parsePrimaryExpression(); - // Multiple allowed values? - if ($stream->test(\Twig_Token::OPERATOR_TYPE, 'or')) - { - $stream->next(); - } - else - { - break; - } + $values = []; + + while (true) { + $values[] = $expressionParser->parsePrimaryExpression(); + // Multiple allowed values? + if ($stream->test(\Twig_Token::OPERATOR_TYPE, 'or')) { + $stream->next(); + } else { + break; } - - $stream->expect(\Twig_Token::BLOCK_END_TYPE); - $body = $this->parser->subparse(array($this, 'decideIfFork')); - $cases[] = new \Twig_Node(array( - 'values' => new \Twig_Node($values), - 'body' => $body - )); - break; } + + $stream->expect(\Twig_Token::BLOCK_END_TYPE); + $body = $this->parser->subparse(array($this, 'decideIfFork')); + $cases[] = new \Twig_Node([ + 'values' => new \Twig_Node($values), + 'body' => $body + ]); + break; + case 'default': - { - $stream->expect(\Twig_Token::BLOCK_END_TYPE); - $default = $this->parser->subparse(array($this, 'decideIfEnd')); - break; - } + $stream->expect(\Twig_Token::BLOCK_END_TYPE); + $default = $this->parser->subparse(array($this, 'decideIfEnd')); + break; + case 'endswitch': - { - $end = true; - break; - } + $end = true; + break; + default: - { - throw new \Twig_Error_Syntax(sprintf('Unexpected end of template. Twig was looking for the following tags "case", "default", or "endswitch" to close the "switch" block started at line %d)', $lineno), -1); - } + throw new \Twig_Error_Syntax(sprintf('Unexpected end of template. Twig was looking for the following tags "case", "default", or "endswitch" to close the "switch" block started at line %d)', $lineno), -1); } } @@ -127,7 +115,6 @@ public function decideIfEnd(\Twig_Token $token) return $token->test(array('endswitch')); } - /** * {@inheritdoc} */ diff --git a/system/src/Grav/Common/Twig/Twig.php b/system/src/Grav/Common/Twig/Twig.php index a47a2b7aa8..a69c1623db 100644 --- a/system/src/Grav/Common/Twig/Twig.php +++ b/system/src/Grav/Common/Twig/Twig.php @@ -113,7 +113,10 @@ public function init() $params['cache'] = new \Twig_Cache_Filesystem($cachePath, \Twig_Cache_Filesystem::FORCE_BYTECODE_INVALIDATION); } - if (!empty($this->autoescape)) { + if (!$config->get('system.strict_mode.twig_compat', true)) { + // Force autoescape on for all files if in strict mode. + $params['autoescape'] = true; + } elseif (!empty($this->autoescape)) { $params['autoescape'] = $this->autoescape; } @@ -238,7 +241,7 @@ public function processPage(Page $item, $content = null) // Process Modular Twig if ($item->modularTwig()) { $twig_vars['content'] = $content; - $extension = $this->grav['uri']->extension(); + $extension = $item->templateFormat(); $extension = $extension ? ".{$extension}.twig" : TEMPLATE_EXT; $template = $item->template() . $extension; $output = $content = $local_twig->render($template, $twig_vars); diff --git a/system/src/Grav/Common/Twig/TwigExtension.php b/system/src/Grav/Common/Twig/TwigExtension.php index fced9158b8..3152656dc5 100644 --- a/system/src/Grav/Common/Twig/TwigExtension.php +++ b/system/src/Grav/Common/Twig/TwigExtension.php @@ -72,7 +72,7 @@ public function getFilters() new \Twig_SimpleFilter('fieldName', [$this, 'fieldNameFilter']), new \Twig_SimpleFilter('ksort', [$this, 'ksortFilter']), new \Twig_SimpleFilter('ltrim', [$this, 'ltrimFilter']), - new \Twig_SimpleFilter('markdown', [$this, 'markdownFunction']), + new \Twig_SimpleFilter('markdown', [$this, 'markdownFunction'], ['is_safe' => ['html']]), new \Twig_SimpleFilter('md5', [$this, 'md5Filter']), new \Twig_SimpleFilter('base32_encode', [$this, 'base32EncodeFilter']), new \Twig_SimpleFilter('base32_decode', [$this, 'base32DecodeFilter']), @@ -88,9 +88,6 @@ public function getFilters() new \Twig_SimpleFilter('safe_truncate_html', ['\Grav\Common\Utils', 'safeTruncateHTML']), new \Twig_SimpleFilter('sort_by_key', [$this, 'sortByKeyFilter']), new \Twig_SimpleFilter('starts_with', [$this, 'startsWithFilter']), - new \Twig_SimpleFilter('t', [$this, 'translate']), - new \Twig_SimpleFilter('tl', [$this, 'translateLanguage']), - new \Twig_SimpleFilter('ta', [$this, 'translateArray']), new \Twig_SimpleFilter('truncate', ['\Grav\Common\Utils', 'truncate']), new \Twig_SimpleFilter('truncate_html', ['\Grav\Common\Utils', 'truncateHTML']), new \Twig_SimpleFilter('json_decode', [$this, 'jsonDecodeFilter']), @@ -100,6 +97,18 @@ public function getFilters() new \Twig_SimpleFilter('print_r', 'print_r'), new \Twig_SimpleFilter('yaml_encode', [$this, 'yamlEncodeFilter']), new \Twig_SimpleFilter('yaml_decode', [$this, 'yamlDecodeFilter']), + + // Translations + new \Twig_SimpleFilter('t', [$this, 'translate']), + new \Twig_SimpleFilter('tl', [$this, 'translateLanguage']), + new \Twig_SimpleFilter('ta', [$this, 'translateArray']), + + // Casting values + new \Twig_SimpleFilter('string', [$this, 'stringFilter']), + new \Twig_SimpleFilter('int', [$this, 'intFilter'], ['is_safe' => true]), + new \Twig_SimpleFilter('bool', [$this, 'boolFilter']), + new \Twig_SimpleFilter('float', [$this, 'floatFilter'], ['is_safe' => true]), + new \Twig_SimpleFilter('array', [$this, 'arrayFilter']), ]; } @@ -111,7 +120,7 @@ public function getFilters() public function getFunctions() { return [ - new \Twig_SimpleFunction('array', [$this, 'arrayFunc']), + new \Twig_SimpleFunction('array', [$this, 'arrayFilter']), new \Twig_SimpleFunction('array_key_value', [$this, 'arrayKeyValueFunc']), new \Twig_SimpleFunction('array_key_exists', 'array_key_exists'), new \Twig_SimpleFunction('array_unique', 'array_unique'), @@ -132,9 +141,6 @@ public function getFunctions() new \Twig_SimpleFunction('regex_replace', [$this, 'regexReplace']), new \Twig_SimpleFunction('regex_filter', [$this, 'regexFilter']), new \Twig_SimpleFunction('string', [$this, 'stringFunc']), - new \Twig_simpleFunction('t', [$this, 'translate']), - new \Twig_simpleFunction('tl', [$this, 'translateLanguage']), - new \Twig_simpleFunction('ta', [$this, 'translateArray']), new \Twig_SimpleFunction('url', [$this, 'urlFunc']), new \Twig_SimpleFunction('json_decode', [$this, 'jsonDecodeFilter']), new \Twig_SimpleFunction('get_cookie', [$this, 'getCookie']), @@ -151,6 +157,10 @@ public function getFunctions() new \Twig_SimpleFunction('nicefilesize', [$this, 'niceFilesizeFunc']), new \Twig_SimpleFunction('nicetime', [$this, 'nicetimeFilter']), + // Translations + new \Twig_simpleFunction('t', [$this, 'translate']), + new \Twig_simpleFunction('tl', [$this, 'translateLanguage']), + new \Twig_simpleFunction('ta', [$this, 'translateArray']), ]; } @@ -617,6 +627,62 @@ public function ltrimFilter($value, $chars = null) return ltrim($value, $chars); } + /** + * Casts input to string. + * + * @param mixed $input + * @return string + */ + public function stringFilter($input) + { + return (string) $input; + } + + + /** + * Casts input to int. + * + * @param mixed $input + * @return int + */ + public function intFilter($input) + { + return (int) $input; + } + + /** + * Casts input to bool. + * + * @param mixed $input + * @return bool + */ + public function boolFilter($input) + { + return (bool) $input; + } + + /** + * Casts input to float. + * + * @param mixed $input + * @return float + */ + public function floatFilter($input) + { + return (float) $input; + } + + /** + * Casts input to array. + * + * @param mixed $input + * @return array + */ + public function arrayFilter($input) + { + return (array) $input; + } + /** * @return mixed */ @@ -693,7 +759,6 @@ public function evaluateTwigFunc($context, $twig ) { $template = $env->createTemplate($twig); return $template->render($context); -; } /** @@ -748,7 +813,7 @@ public function dump(\Twig_Environment $env, $context) * Output a Gist * * @param string $id - * @param string $file + * @param string|bool $file * * @return string */ @@ -788,19 +853,6 @@ public static function padFilter($input, $pad_length, $pad_string = " ", $pad_ty return str_pad($input, (int)$pad_length, $pad_string, $pad_type); } - - /** - * Cast a value to array - * - * @param $value - * - * @return array - */ - public function arrayFunc($value) - { - return (array)$value; - } - /** * Workaround for twig associative array initialization * Returns a key => val array @@ -976,7 +1028,7 @@ public function regexFilter($array, $regex, $flags = 0) { public function redirectFunc($url, $statusCode = 303) { header('Location: ' . $url, true, $statusCode); - die(); + exit(); } /** @@ -1060,7 +1112,7 @@ public function readFileFunc($filepath) if (file_exists($filepath)) { return file_get_contents($filepath); - } + } return false; } diff --git a/system/src/Grav/Common/Uri.php b/system/src/Grav/Common/Uri.php index 8017ecb884..4bb6bc7b1d 100644 --- a/system/src/Grav/Common/Uri.php +++ b/system/src/Grav/Common/Uri.php @@ -11,6 +11,7 @@ use Grav\Common\Config\Config; use Grav\Common\Language\Language; use Grav\Common\Page\Page; +use Grav\Common\Page\Pages; use Grav\Framework\Route\RouteFactory; use Grav\Framework\Uri\UriFactory; use Grav\Framework\Uri\UriPartsFilter; @@ -156,12 +157,6 @@ public function init() $uri = preg_replace('|^' . preg_quote($setup_base, '|') . '|', '', $uri); } - // If configured to, redirect trailing slash URI's with a 302 redirect - $redirect = str_replace($this->root, '', rtrim($uri, '/')); - if ($redirect && $uri !== '/' && $redirect !== $this->base() && $config->get('system.pages.redirect_trailing_slash', false) && Utils::endsWith($uri, '/')) { - $grav->redirect($redirect, 302); - } - // process params $uri = $this->processParams($uri, $config->get('system.param_sep')); @@ -206,9 +201,9 @@ public function init() } // Set some Grav stuff - $grav['base_url_absolute'] = $grav['config']->get('system.custom_base_url') ?: $this->rootUrl(true); + $grav['base_url_absolute'] = $config->get('system.custom_base_url') ?: $this->rootUrl(true); $grav['base_url_relative'] = $this->rootUrl(false); - $grav['base_url'] = $grav['config']->get('system.absolute_urls') ? $grav['base_url_absolute'] : $grav['base_url_relative']; + $grav['base_url'] = $config->get('system.absolute_urls') ? $grav['base_url_absolute'] : $grav['base_url_relative']; RouteFactory::setRoot($this->root_path); RouteFactory::setLanguage($language->getLanguageURLPrefix()); @@ -376,6 +371,17 @@ public function extension($default = null) return $this->extension; } + public function method() + { + $method = isset($_SERVER['REQUEST_METHOD']) ? strtoupper($_SERVER['REQUEST_METHOD']) : 'GET'; + + if ($method === 'POST' && isset($_SERVER['X-HTTP-METHOD-OVERRIDE'])) { + $method = strtoupper($_SERVER['X-HTTP-METHOD-OVERRIDE']); + } + + return $method; + } + /** * Return the scheme of the URI * @@ -481,11 +487,9 @@ public function uri($include_root = true) { if ($include_root) { return $this->uri; - } else { - $uri = str_replace($this->root_path, '', $this->uri); - return $uri; } + return str_replace($this->root_path, '', $this->uri); } /** @@ -508,16 +512,10 @@ public function baseIncludingLanguage() { $grav = Grav::instance(); - // Link processing should prepend language - $language = $grav['language']; - $language_append = ''; - if ($language->enabled()) { - $language_append = $language->getLanguageURLPrefix(); - } + /** @var Pages $pages */ + $pages = $grav['pages']; - $base = $grav['base_url_relative']; - - return rtrim($base . $grav['pages']->base(), '/') . $language_append; + return $pages->baseUrl(null, false); } /** @@ -633,10 +631,9 @@ public static function ip() } return $ip; - } - /** + /** * Returns current Uri. * * @return \Grav\Framework\Uri\Uri @@ -883,7 +880,26 @@ public static function convertUrl(Page $page, $url, $type = 'link', $absolute = public static function parseUrl($url) { $grav = Grav::instance(); - $parts = parse_url($url); + + $encodedUrl = preg_replace_callback( + '%[^:/@?&=#]+%usD', + function ($matches) { return rawurlencode($matches[0]); }, + $url + ); + + $parts = parse_url($encodedUrl); + + if (false === $parts) { + return false; + } + + foreach($parts as $name => $value) { + $parts[$name] = rawurldecode($value); + } + + if (!isset($parts['path'])) { + $parts['path'] = ''; + } list($stripped_path, $params) = static::extractParams($parts['path'], $grav['config']->get('system.param_sep')); @@ -1262,7 +1278,7 @@ public function post($element = null, $filter_type = null) { if (!$this->post) { $content_type = $this->getContentType(); - if ($content_type == 'application/json') { + if ($content_type === 'application/json') { $json = file_get_contents('php://input'); $this->post = json_decode($json, true); } elseif (!empty($_POST)) { @@ -1270,7 +1286,7 @@ public function post($element = null, $filter_type = null) } } - if ($this->post && !is_null($element)) { + if ($this->post && null !== $element) { $item = Utils::getDotNotation($this->post, $element); if ($filter_type) { $item = filter_var($item, $filter_type); diff --git a/system/src/Grav/Common/Utils.php b/system/src/Grav/Common/Utils.php index 02d5e158a3..0f27277b3c 100644 --- a/system/src/Grav/Common/Utils.php +++ b/system/src/Grav/Common/Utils.php @@ -45,8 +45,20 @@ public static function url($input, $domain = false) /** @var UniformResourceLocator $locator */ $locator = Grav::instance()['locator']; - // Get relative path to the resource (or false if not found). - $resource = $locator->findResource($input, false); + $parts = Uri::parseUrl($input); + + if ($parts) { + $resource = $locator->findResource("{$parts['scheme']}://{$parts['host']}{$parts['path']}", false); + + if (isset($parts['query'])) { + $resource = $resource . '?' . $parts['query']; + } + } else { + // Not a valid URL (can still be a stream). + $resource = $locator->findResource($input, false); + } + + } else { $resource = $input; } @@ -262,7 +274,7 @@ public static function truncate($string, $limit = 150, $up_to_break = false, $br // is $break present between $limit and the end of the string? if ($up_to_break && false !== ($breakpoint = mb_strpos($string, $break, $limit))) { if ($breakpoint < mb_strlen($string) - 1) { - $string = mb_substr($string, 0, $breakpoint) . $break; + $string = mb_substr($string, 0, $breakpoint) . $pad; } } else { $string = mb_substr($string, 0, $limit) . $pad; @@ -705,11 +717,11 @@ public static function isPositive($value) * with reverse proxy setups. * * @param string $action - * @param bool $plusOneTick if true, generates the token for the next tick (the next 12 hours) + * @param bool $previousTick if true, generates the token for the previous tick (the previous 12 hours) * * @return string the nonce string */ - private static function generateNonceString($action, $plusOneTick = false) + private static function generateNonceString($action, $previousTick = false) { $username = ''; if (isset(Grav::instance()['user'])) { @@ -720,29 +732,8 @@ private static function generateNonceString($action, $plusOneTick = false) $token = session_id(); $i = self::nonceTick(); - if ($plusOneTick) { - $i++; - } - - return ($i . '|' . $action . '|' . $username . '|' . $token . '|' . Grav::instance()['config']->get('security.salt')); - } - - //Added in version 1.0.8 to ensure that existing nonces are not broken. - private static function generateNonceStringOldStyle($action, $plusOneTick = false) - { - if (isset(Grav::instance()['user'])) { - $user = Grav::instance()['user']; - $username = $user->username; - if (isset($_SERVER['REMOTE_ADDR'])) { - $username .= $_SERVER['REMOTE_ADDR']; - } - } else { - $username = isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : ''; - } - $token = session_id(); - $i = self::nonceTick(); - if ($plusOneTick) { - $i++; + if ($previousTick) { + $i--; } return ($i . '|' . $action . '|' . $username . '|' . $token . '|' . Grav::instance()['config']->get('security.salt')); @@ -768,33 +759,20 @@ private static function nonceTick() * action is the same for 12 hours. * * @param string $action the action the nonce is tied to (e.g. save-user-admin or move-page-homepage) - * @param bool $plusOneTick if true, generates the token for the next tick (the next 12 hours) + * @param bool $previousTick if true, generates the token for the previous tick (the previous 12 hours) * * @return string the nonce */ - public static function getNonce($action, $plusOneTick = false) - { - // Don't regenerate this again if not needed - if (isset(static::$nonces[$action])) { - return static::$nonces[$action]; - } - $nonce = md5(self::generateNonceString($action, $plusOneTick)); - static::$nonces[$action] = $nonce; - - return static::$nonces[$action]; - } - - //Added in version 1.0.8 to ensure that existing nonces are not broken. - public static function getNonceOldStyle($action, $plusOneTick = false) + public static function getNonce($action, $previousTick = false) { // Don't regenerate this again if not needed - if (isset(static::$nonces[$action])) { - return static::$nonces[$action]; + if (isset(static::$nonces[$action][$previousTick])) { + return static::$nonces[$action][$previousTick]; } - $nonce = md5(self::generateNonceStringOldStyle($action, $plusOneTick)); - static::$nonces[$action] = $nonce; + $nonce = md5(self::generateNonceString($action, $previousTick)); + static::$nonces[$action][$previousTick] = $nonce; - return static::$nonces[$action]; + return static::$nonces[$action][$previousTick]; } /** @@ -818,20 +796,8 @@ public static function verifyNonce($nonce, $action) } //Nonce generated 12-24 hours ago - $plusOneTick = true; - if ($nonce === self::getNonce($action, $plusOneTick)) { - return true; - } - - //Added in version 1.0.8 to ensure that existing nonces are not broken. - //Nonce generated 0-12 hours ago - if ($nonce === self::getNonceOldStyle($action)) { - return true; - } - - //Nonce generated 12-24 hours ago - $plusOneTick = true; - if ($nonce === self::getNonceOldStyle($action, $plusOneTick)) { + $previousTick = true; + if ($nonce === self::getNonce($action, $previousTick)) { return true; } diff --git a/system/src/Grav/Console/Cli/InstallCommand.php b/system/src/Grav/Console/Cli/InstallCommand.php index ac00256dd5..afcd80c02e 100644 --- a/system/src/Grav/Console/Cli/InstallCommand.php +++ b/system/src/Grav/Console/Cli/InstallCommand.php @@ -9,9 +9,9 @@ namespace Grav\Console\Cli; use Grav\Console\ConsoleCommand; +use RocketTheme\Toolbox\File\YamlFile; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputOption; -use Symfony\Component\Yaml\Yaml; class InstallCommand extends ConsoleCommand { @@ -71,9 +71,9 @@ protected function serve() // Look for dependencies file in ROOT and USER dir if (file_exists($this->user_path . $dependencies_file)) { - $this->config = Yaml::parse(file_get_contents($this->user_path . $dependencies_file)); + $file = YamlFile::instance($this->user_path . $dependencies_file); } elseif (file_exists($this->destination . $dependencies_file)) { - $this->config = Yaml::parse(file_get_contents($this->destination . $dependencies_file)); + $file = YamlFile::instance($this->destination . $dependencies_file); } else { $this->output->writeln('ERROR Missing .dependencies file in user/ folder'); if ($this->input->getArgument('destination')) { @@ -85,6 +85,9 @@ protected function serve() return; } + $this->config = $file->content(); + $file->free(); + // If yaml config, process if ($this->config) { if (!$this->input->getOption('symlink')) { @@ -153,23 +156,32 @@ private function symlink() exec('cd ' . $this->destination); foreach ($this->config['links'] as $repo => $data) { - $from = $this->local_config[$data['scm'] . '_repos'] . $data['src']; + $repos = (array) $this->local_config[$data['scm'] . '_repos']; + $from = false; $to = $this->destination . $data['path']; - if (file_exists($from)) { - if (!file_exists($to)) { - symlink($from, $to); - $this->output->writeln('SUCCESS symlinked ' . $data['src'] . ' -> ' . $data['path'] . ''); - $this->output->writeln(''); - } else { - $this->output->writeln('destination: ' . $to . ' already exists, skipping...'); - $this->output->writeln(''); + foreach ($repos as $repo) { + $path = $repo . $data['src']; + if (file_exists($path)) { + $from = $path; + continue; } - } else { + } + + if (!$from) { $this->output->writeln('source: ' . $from . ' does not exists, skipping...'); $this->output->writeln(''); } + if (!file_exists($to)) { + symlink($from, $to); + $this->output->writeln('SUCCESS symlinked ' . $data['src'] . ' -> ' . $data['path'] . ''); + $this->output->writeln(''); + } else { + $this->output->writeln('destination: ' . $to . ' already exists, skipping...'); + $this->output->writeln(''); + } + } } } diff --git a/system/src/Grav/Console/ConsoleTrait.php b/system/src/Grav/Console/ConsoleTrait.php index b6ab3bfe0d..4498172450 100644 --- a/system/src/Grav/Console/ConsoleTrait.php +++ b/system/src/Grav/Console/ConsoleTrait.php @@ -12,11 +12,11 @@ use Grav\Common\Composer; use Grav\Common\GravTrait; use Grav\Console\Cli\ClearCacheCommand; +use RocketTheme\Toolbox\File\YamlFile; use Symfony\Component\Console\Formatter\OutputFormatterStyle; use Symfony\Component\Console\Input\ArrayInput; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; -use Symfony\Component\Yaml\Yaml; trait ConsoleTrait { @@ -123,7 +123,9 @@ public function loadLocalConfig() $local_config_file = $home_folder . '/.grav/config'; if (file_exists($local_config_file)) { - $this->local_config = Yaml::parse(file_get_contents($local_config_file)); + $file = YamlFile::instance($local_config_file); + $this->local_config = $file->content(); + $file->free(); return $local_config_file; } diff --git a/system/src/Grav/Console/Gpm/InstallCommand.php b/system/src/Grav/Console/Gpm/InstallCommand.php index d7e932e4ec..0311428254 100644 --- a/system/src/Grav/Console/Gpm/InstallCommand.php +++ b/system/src/Grav/Console/Gpm/InstallCommand.php @@ -444,18 +444,21 @@ private function getSymlinkSource($package) { $matches = $this->getGitRegexMatches($package); - foreach ($this->local_config as $path) { + foreach ($this->local_config as $paths) { if (Utils::endsWith($matches[2], '.git')) { $repo_dir = preg_replace('/\.git$/', '', $matches[2]); } else { $repo_dir = $matches[2]; } - - $from = rtrim($path, '/') . '/' . $repo_dir; - - if (file_exists($from)) { - return $from; + + $paths = (array) $paths; + foreach ($paths as $repo) { + $path = rtrim($repo, '/') . '/' . $repo_dir; + if (file_exists($path)) { + return $path; + } } + } return false; diff --git a/system/src/Grav/Console/Gpm/VersionCommand.php b/system/src/Grav/Console/Gpm/VersionCommand.php index c828106c48..eb7872ead8 100644 --- a/system/src/Grav/Console/Gpm/VersionCommand.php +++ b/system/src/Grav/Console/Gpm/VersionCommand.php @@ -11,9 +11,9 @@ use Grav\Common\GPM\GPM; use Grav\Common\GPM\Upgrader; use Grav\Console\ConsoleCommand; +use RocketTheme\Toolbox\File\YamlFile; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputOption; -use Symfony\Component\Yaml\Yaml; class VersionCommand extends ConsoleCommand { @@ -84,7 +84,10 @@ protected function serve() } } - $package_yaml = Yaml::parse(file_get_contents($blueprints_path)); + $file = YamlFile::instance($blueprints_path); + $package_yaml = $file->content(); + $file->free(); + $version = $package_yaml['version']; if (!$version) { diff --git a/system/src/Grav/Framework/Cache/CacheTrait.php b/system/src/Grav/Framework/Cache/CacheTrait.php index 65f4d8d40b..2f2b178baa 100644 --- a/system/src/Grav/Framework/Cache/CacheTrait.php +++ b/system/src/Grav/Framework/Cache/CacheTrait.php @@ -16,21 +16,18 @@ */ trait CacheTrait { - /** - * @var string - */ + /** @var string */ private $namespace = ''; - /** - * @var int|null - */ + /** @var int|null */ private $defaultLifetime = null; - /** - * @var \stdClass - */ + /** @var \stdClass */ private $miss; + /** @var bool */ + private $validation = true; + /** * Always call from constructor. * @@ -45,6 +42,14 @@ protected function init($namespace = '', $defaultLifetime = null) $this->miss = new \stdClass; } + /** + * @param $validation + */ + public function setValidation($validation) + { + $this->validation = (bool) $validation; + } + /** * @return string */ @@ -307,6 +312,10 @@ protected function validateKey($key) */ protected function validateKeys($keys) { + if (!$this->validation) { + return; + } + foreach ($keys as $key) { $this->validateKey($key); } diff --git a/system/src/Grav/Framework/Collection/ArrayCollection.php b/system/src/Grav/Framework/Collection/ArrayCollection.php index bf6dda056d..4c922f4f05 100644 --- a/system/src/Grav/Framework/Collection/ArrayCollection.php +++ b/system/src/Grav/Framework/Collection/ArrayCollection.php @@ -24,11 +24,6 @@ class ArrayCollection extends BaseArrayCollection implements CollectionInterface */ public function reverse() { - // TODO: remove when PHP 5.6 is minimum (with doctrine/collections v1.4). - if (!method_exists($this, 'createFrom')) { - return new static(array_reverse($this->toArray())); - } - return $this->createFrom(array_reverse($this->toArray())); } @@ -42,11 +37,6 @@ public function shuffle() $keys = $this->getKeys(); shuffle($keys); - // TODO: remove when PHP 5.6 is minimum (with doctrine/collections v1.4). - if (!method_exists($this, 'createFrom')) { - return new static(array_replace(array_flip($keys), $this->toArray())); - } - return $this->createFrom(array_replace(array_flip($keys), $this->toArray())); } diff --git a/system/src/Grav/Framework/ContentBlock/ContentBlock.php b/system/src/Grav/Framework/ContentBlock/ContentBlock.php index 6229b81518..672dba4cc6 100644 --- a/system/src/Grav/Framework/ContentBlock/ContentBlock.php +++ b/system/src/Grav/Framework/ContentBlock/ContentBlock.php @@ -27,6 +27,7 @@ class ContentBlock implements ContentBlockInterface protected $tokenTemplate = '@@BLOCK-%s@@'; protected $content = ''; protected $blocks = []; + protected $checksum; /** * @param string $id @@ -40,6 +41,7 @@ public static function create($id = null) /** * @param array $serialized * @return ContentBlockInterface + * @throws \InvalidArgumentException */ public static function fromArray(array $serialized) { @@ -48,14 +50,14 @@ public static function fromArray(array $serialized) $id = isset($serialized['id']) ? $serialized['id'] : null; if (!$type || !$id || !is_a($type, 'Grav\Framework\ContentBlock\ContentBlockInterface', true)) { - throw new \RuntimeException('Bad data'); + throw new \InvalidArgumentException('Bad data'); } /** @var ContentBlockInterface $instance */ $instance = new $type($id); $instance->build($serialized); } catch (\Exception $e) { - throw new \RuntimeException(sprintf('Cannot unserialize Block: %s', $e->getMessage()), $e->getCode(), $e); + throw new \InvalidArgumentException(sprintf('Cannot unserialize Block: %s', $e->getMessage()), $e->getCode(), $e); } return $instance; @@ -104,9 +106,13 @@ public function toArray() $array = [ '_type' => get_class($this), '_version' => $this->version, - 'id' => $this->id, + 'id' => $this->id ]; + if ($this->checksum) { + $array['checksum'] = $this->checksum; + } + if ($this->content) { $array['content'] = $this->content; } @@ -158,6 +164,7 @@ public function build(array $serialized) $this->checkVersion($serialized); $this->id = isset($serialized['id']) ? $serialized['id'] : $this->generateId(); + $this->checksum = isset($serialized['checksum']) ? $serialized['checksum'] : null; if (isset($serialized['content'])) { $this->setContent($serialized['content']); @@ -169,6 +176,25 @@ public function build(array $serialized) } } + /** + * @param string $checksum + * @return $this + */ + public function setChecksum($checksum) + { + $this->checksum = $checksum; + + return $this; + } + + /** + * @return string + */ + public function getChecksum() + { + return $this->checksum; + } + /** * @param string $content * @return $this @@ -222,7 +248,7 @@ protected function generateId() */ protected function checkVersion(array $serialized) { - $version = isset($serialized['_version']) ? (string) $serialized['_version'] : '1'; + $version = isset($serialized['_version']) ? (int) $serialized['_version'] : 1; if ($version !== $this->version) { throw new \RuntimeException(sprintf('Unsupported version %s', $version)); } diff --git a/system/src/Grav/Framework/ContentBlock/ContentBlockInterface.php b/system/src/Grav/Framework/ContentBlock/ContentBlockInterface.php index 4f2434c9d2..fff8f2e73f 100644 --- a/system/src/Grav/Framework/ContentBlock/ContentBlockInterface.php +++ b/system/src/Grav/Framework/ContentBlock/ContentBlockInterface.php @@ -61,6 +61,17 @@ public function __toString(); */ public function build(array $serialized); + /** + * @param string $checksum + * @return $this + */ + public function setChecksum($checksum); + + /** + * @return string + */ + public function getChecksum(); + /** * @param string $content * @return $this diff --git a/system/src/Grav/Framework/ContentBlock/HtmlBlock.php b/system/src/Grav/Framework/ContentBlock/HtmlBlock.php index 976018a0e0..3fd345b8c7 100644 --- a/system/src/Grav/Framework/ContentBlock/HtmlBlock.php +++ b/system/src/Grav/Framework/ContentBlock/HtmlBlock.php @@ -15,6 +15,7 @@ */ class HtmlBlock extends ContentBlock implements HtmlBlockInterface { + protected $version = 1; protected $frameworks = []; protected $styles = []; protected $scripts = []; diff --git a/system/src/Grav/Framework/File/Formatter/FormatterInterface.php b/system/src/Grav/Framework/File/Formatter/FormatterInterface.php new file mode 100644 index 0000000000..273bf7873a --- /dev/null +++ b/system/src/Grav/Framework/File/Formatter/FormatterInterface.php @@ -0,0 +1,44 @@ +config = $config + [ + 'file_extension' => '.ini' + ]; + } + + /** + * @deprecated 1.5 Use $formatter->getDefaultFileExtension() instead. + */ + public function getFileExtension() + { + return $this->getDefaultFileExtension(); + } + + /** + * {@inheritdoc} + */ + public function getDefaultFileExtension() + { + $extensions = $this->getSupportedFileExtensions(); + + return (string) reset($extensions); + } + + /** + * {@inheritdoc} + */ + public function getSupportedFileExtensions() + { + return (array) $this->config['file_extension']; + } + + /** + * {@inheritdoc} + */ + public function encode($data) + { + $string = ''; + foreach ($data as $key => $value) { + $string .= $key . '="' . preg_replace( + ['/"/', '/\\\/', "/\t/", "/\n/", "/\r/"], + ['\"', '\\\\', '\t', '\n', '\r'], + $value + ) . "\"\n"; + } + + return $string; + } + + /** + * {@inheritdoc} + */ + public function decode($data) + { + $decoded = @parse_ini_string($data); + + if ($decoded === false) { + throw new \RuntimeException('Decoding INI failed'); + } + + return $decoded; + } +} diff --git a/system/src/Grav/Framework/File/Formatter/JsonFormatter.php b/system/src/Grav/Framework/File/Formatter/JsonFormatter.php new file mode 100644 index 0000000000..f5705e3814 --- /dev/null +++ b/system/src/Grav/Framework/File/Formatter/JsonFormatter.php @@ -0,0 +1,78 @@ +config = $config + [ + 'file_extension' => '.json', + 'encode_options' => 0, + 'decode_assoc' => true + ]; + } + + /** + * @deprecated 1.5 Use $formatter->getDefaultFileExtension() instead. + */ + public function getFileExtension() + { + return $this->getDefaultFileExtension(); + } + + /** + * {@inheritdoc} + */ + public function getDefaultFileExtension() + { + $extensions = $this->getSupportedFileExtensions(); + + return (string) reset($extensions); + } + + /** + * {@inheritdoc} + */ + public function getSupportedFileExtensions() + { + return (array) $this->config['file_extension']; + } + + /** + * {@inheritdoc} + */ + public function encode($data) + { + $encoded = @json_encode($data, $this->config['encode_options']); + + if ($encoded === false) { + throw new \RuntimeException('Encoding JSON failed'); + } + + return $encoded; + } + + /** + * {@inheritdoc} + */ + public function decode($data) + { + $decoded = @json_decode($data, $this->config['decode_assoc']); + + if ($decoded === false) { + throw new \RuntimeException('Decoding JSON failed'); + } + + return $decoded; + } +} diff --git a/system/src/Grav/Framework/File/Formatter/MarkdownFormatter.php b/system/src/Grav/Framework/File/Formatter/MarkdownFormatter.php new file mode 100644 index 0000000000..9cccba9903 --- /dev/null +++ b/system/src/Grav/Framework/File/Formatter/MarkdownFormatter.php @@ -0,0 +1,116 @@ +config = $config + [ + 'file_extension' => '.md', + 'header' => 'header', + 'body' => 'markdown', + 'raw' => 'frontmatter', + 'yaml' => ['inline' => 20] + ]; + + $this->headerFormatter = $headerFormatter ?: new YamlFormatter($this->config['yaml']); + } + + /** + * @deprecated 1.5 Use $formatter->getDefaultFileExtension() instead. + */ + public function getFileExtension() + { + return $this->getDefaultFileExtension(); + } + + /** + * {@inheritdoc} + */ + public function getDefaultFileExtension() + { + $extensions = $this->getSupportedFileExtensions(); + + return (string) reset($extensions); + } + + /** + * {@inheritdoc} + */ + public function getSupportedFileExtensions() + { + return (array) $this->config['file_extension']; + } + + /** + * {@inheritdoc} + */ + public function encode($data) + { + $headerVar = $this->config['header']; + $bodyVar = $this->config['body']; + + $header = isset($data[$headerVar]) ? (array) $data[$headerVar] : []; + $body = isset($data[$bodyVar]) ? (string) $data[$bodyVar] : ''; + + // Create Markdown file with YAML header. + $encoded = ''; + if ($header) { + $encoded = "---\n" . trim($this->headerFormatter->encode($data['header'])) . "\n---\n\n"; + } + $encoded .= $body; + + // Normalize line endings to Unix style. + $encoded = preg_replace("/(\r\n|\r)/", "\n", $encoded); + + return $encoded; + } + + /** + * {@inheritdoc} + */ + public function decode($data) + { + $headerVar = $this->config['header']; + $bodyVar = $this->config['body']; + $rawVar = $this->config['raw']; + + $content = [ + $headerVar => [], + $bodyVar => '' + ]; + + $headerRegex = "/^---\n(.+?)\n---\n{0,}(.*)$/uis"; + + // Normalize line endings to Unix style. + $data = preg_replace("/(\r\n|\r)/", "\n", $data); + + // Parse header. + preg_match($headerRegex, ltrim($data), $matches); + if(empty($matches)) { + $content[$bodyVar] = $data; + } else { + // Normalize frontmatter. + $frontmatter = preg_replace("/\n\t/", "\n ", $matches[1]); + if ($rawVar) { + $content[$rawVar] = $frontmatter; + } + $content[$headerVar] = $this->headerFormatter->decode($frontmatter); + $content[$bodyVar] = $matches[2]; + } + + return $content; + } +} diff --git a/system/src/Grav/Framework/File/Formatter/SerializeFormatter.php b/system/src/Grav/Framework/File/Formatter/SerializeFormatter.php new file mode 100644 index 0000000000..5fd20eb895 --- /dev/null +++ b/system/src/Grav/Framework/File/Formatter/SerializeFormatter.php @@ -0,0 +1,96 @@ +config = $config + [ + 'file_extension' => '.ser' + ]; + } + + /** + * @deprecated 1.5 Use $formatter->getDefaultFileExtension() instead. + */ + public function getFileExtension() + { + return $this->getDefaultFileExtension(); + } + + /** + * {@inheritdoc} + */ + public function getDefaultFileExtension() + { + $extensions = $this->getSupportedFileExtensions(); + + return (string) reset($extensions); + } + + /** + * {@inheritdoc} + */ + public function getSupportedFileExtensions() + { + return (array) $this->config['file_extension']; + } + + /** + * {@inheritdoc} + */ + public function encode($data) + { + return serialize($this->preserveLines($data, ["\n", "\r"], ['\\n', '\\r'])); + } + + /** + * {@inheritdoc} + */ + public function decode($data) + { + $decoded = @unserialize($data); + + if ($decoded === false) { + throw new \RuntimeException('Decoding serialized data failed'); + } + + return $this->preserveLines($decoded, ['\\n', '\\r'], ["\n", "\r"]); + } + + /** + * Preserve new lines, recursive function. + * + * @param mixed $data + * @param array $search + * @param array $replace + * @return mixed + */ + protected function preserveLines($data, $search, $replace) + { + if (is_string($data)) { + $data = str_replace($search, $replace, $data); + } elseif (is_array($data)) { + foreach ($data as &$value) { + $value = $this->preserveLines($value, $search, $replace); + } + unset($value); + } + + return $data; + } +} \ No newline at end of file diff --git a/system/src/Grav/Framework/File/Formatter/YamlFormatter.php b/system/src/Grav/Framework/File/Formatter/YamlFormatter.php new file mode 100644 index 0000000000..858fbea5f5 --- /dev/null +++ b/system/src/Grav/Framework/File/Formatter/YamlFormatter.php @@ -0,0 +1,103 @@ +config = $config + [ + 'file_extension' => '.yaml', + 'inline' => 5, + 'indent' => 2, + 'native' => true, + 'compat' => true + ]; + } + + /** + * @deprecated 1.5 Use $formatter->getDefaultFileExtension() instead. + */ + public function getFileExtension() + { + return $this->getDefaultFileExtension(); + } + + /** + * {@inheritdoc} + */ + public function getDefaultFileExtension() + { + $extensions = $this->getSupportedFileExtensions(); + + return (string) reset($extensions); + } + + /** + * {@inheritdoc} + */ + public function getSupportedFileExtensions() + { + return (array) $this->config['file_extension']; + } + + /** + * {@inheritdoc} + */ + public function encode($data) + { + try { + return (string) YamlParser::dump( + $data, + $this->config['inline'], + $this->config['indent'], + YamlParser::DUMP_EXCEPTION_ON_INVALID_TYPE + ); + } catch (DumpException $e) { + throw new \RuntimeException('Encoding YAML failed: ' . $e->getMessage(), 0, $e); + } + } + + /** + * {@inheritdoc} + */ + public function decode($data) + { + // Try native PECL YAML PHP extension first if available. + if ($this->config['native'] && function_exists('yaml_parse')) { + // Safely decode YAML. + $saved = @ini_get('yaml.decode_php'); + @ini_set('yaml.decode_php', 0); + $decoded = @yaml_parse($data); + @ini_set('yaml.decode_php', $saved); + + if ($decoded !== false) { + return (array) $decoded; + } + } + + try { + return (array) YamlParser::parse($data); + } catch (ParseException $e) { + if ($this->config['compat']) { + return (array) FallbackYamlParser::parse($data); + } + + throw new \RuntimeException('Decoding YAML failed: ' . $e->getMessage(), 0, $e); + } + } +} diff --git a/system/src/Grav/Framework/Object/Base/ObjectCollectionTrait.php b/system/src/Grav/Framework/Object/Base/ObjectCollectionTrait.php index f99a5ff200..6334397f00 100644 --- a/system/src/Grav/Framework/Object/Base/ObjectCollectionTrait.php +++ b/system/src/Grav/Framework/Object/Base/ObjectCollectionTrait.php @@ -32,11 +32,6 @@ public function copy() $list[$key] = is_object($value) ? clone $value : $value; } - // TODO: remove when PHP 5.6 is minimum (with doctrine/collections v1.4). - if (!method_exists($this, 'createFrom')) { - return new static($list); - } - return $this->createFrom($list); } @@ -170,12 +165,7 @@ public function collectionGroup($property) { $collections = []; foreach ($this->group($property) as $id => $elements) { - // TODO: remove when PHP 5.6 is minimum (with doctrine/collections v1.4). - if (!method_exists($this, 'createFrom')) { - $collection = new static($elements); - } else { - $collection = $this->createFrom($elements); - } + $collection = $this->createFrom($elements); $collections[$id] = $collection; } diff --git a/system/src/Grav/Framework/Object/Base/ObjectTrait.php b/system/src/Grav/Framework/Object/Base/ObjectTrait.php index 6522e8eb11..c7363810fb 100644 --- a/system/src/Grav/Framework/Object/Base/ObjectTrait.php +++ b/system/src/Grav/Framework/Object/Base/ObjectTrait.php @@ -15,7 +15,7 @@ */ trait ObjectTrait { - static protected $prefix; + /** @var string */ static protected $type; /** @@ -23,18 +23,28 @@ trait ObjectTrait */ private $_key; + /** + * @return string + */ + protected function getTypePrefix() + { + return ''; + } + /** * @param bool $prefix * @return string */ public function getType($prefix = true) { + $type = $prefix ? $this->getTypePrefix() : ''; + if (static::$type) { - return ($prefix ? static::$prefix : '') . static::$type; + return $type . static::$type; } $class = get_class($this); - return ($prefix ? static::$prefix : '') . strtolower(substr($class, strrpos($class, '\\') + 1)); + return $type . strtolower(substr($class, strrpos($class, '\\') + 1)); } /** @@ -108,7 +118,7 @@ public function defProperty($property, $default) */ public function serialize() { - return serialize($this->jsonSerialize()); + return serialize($this->doSerialize()); } /** @@ -124,6 +134,14 @@ public function unserialize($serialized) $this->doUnserialize($data); } + /** + * @return array + */ + protected function doSerialize() + { + return $this->jsonSerialize(); + } + /** * @param array $serialized */ @@ -159,10 +177,13 @@ public function __toString() /** * @param string $key + * @return $this */ protected function setKey($key) { $this->_key = (string) $key; + + return $this; } abstract protected function doHasProperty($property); diff --git a/system/src/Grav/Framework/Object/Collection/ObjectExpressionVisitor.php b/system/src/Grav/Framework/Object/Collection/ObjectExpressionVisitor.php new file mode 100644 index 0000000000..5ad3897d40 --- /dev/null +++ b/system/src/Grav/Framework/Object/Collection/ObjectExpressionVisitor.php @@ -0,0 +1,198 @@ +{$accessor}(); + break; + } + } + + if ($op) { + $function = 'filter' . ucfirst(strtolower($op)); + if (method_exists(static::class, $function)) { + $value = static::$function($value); + } + } + + return $value; + } + + public static function filterLower($str) + { + return mb_strtolower($str); + } + + public static function filterUpper($str) + { + return mb_strtoupper($str); + } + + public static function filterLength($str) + { + return mb_strlen($str); + } + + public static function filterLtrim($str) + { + return ltrim($str); + } + + public static function filterRtrim($str) + { + return rtrim($str); + } + + public static function filterTrim($str) + { + return trim($str); + } + + /** + * Helper for sorting arrays of objects based on multiple fields + orientations. + * + * @param string $name + * @param int $orientation + * @param \Closure $next + * + * @return \Closure + */ + public static function sortByField($name, $orientation = 1, \Closure $next = null) + { + if (!$next) { + $next = function() { + return 0; + }; + } + + return function ($a, $b) use ($name, $next, $orientation) { + $aValue = static::getObjectFieldValue($a, $name); + $bValue = static::getObjectFieldValue($b, $name); + + if ($aValue === $bValue) { + return $next($a, $b); + } + + return (($aValue > $bValue) ? 1 : -1) * $orientation; + }; + } + + /** + * {@inheritDoc} + */ + public function walkComparison(Comparison $comparison) + { + $field = $comparison->getField(); + $value = $comparison->getValue()->getValue(); // shortcut for walkValue() + + switch ($comparison->getOperator()) { + case Comparison::EQ: + return function ($object) use ($field, $value) { + return static::getObjectFieldValue($object, $field) === $value; + }; + + case Comparison::NEQ: + return function ($object) use ($field, $value) { + return static::getObjectFieldValue($object, $field) !== $value; + }; + + case Comparison::LT: + return function ($object) use ($field, $value) { + return static::getObjectFieldValue($object, $field) < $value; + }; + + case Comparison::LTE: + return function ($object) use ($field, $value) { + return static::getObjectFieldValue($object, $field) <= $value; + }; + + case Comparison::GT: + return function ($object) use ($field, $value) { + return static::getObjectFieldValue($object, $field) > $value; + }; + + case Comparison::GTE: + return function ($object) use ($field, $value) { + return static::getObjectFieldValue($object, $field) >= $value; + }; + + case Comparison::IN: + return function ($object) use ($field, $value) { + return \in_array(static::getObjectFieldValue($object, $field), $value, true); + }; + + case Comparison::NIN: + return function ($object) use ($field, $value) { + return !\in_array(static::getObjectFieldValue($object, $field), $value, true); + }; + + case Comparison::CONTAINS: + return function ($object) use ($field, $value) { + return false !== strpos(static::getObjectFieldValue($object, $field), $value); + }; + + case Comparison::MEMBER_OF: + return function ($object) use ($field, $value) { + $fieldValues = static::getObjectFieldValue($object, $field); + if (!is_array($fieldValues)) { + $fieldValues = iterator_to_array($fieldValues); + } + return \in_array($value, $fieldValues, true); + }; + + case Comparison::STARTS_WITH: + return function ($object) use ($field, $value) { + return 0 === strpos(static::getObjectFieldValue($object, $field), $value); + }; + + case Comparison::ENDS_WITH: + return function ($object) use ($field, $value) { + return $value === substr(static::getObjectFieldValue($object, $field), -strlen($value)); + }; + + + default: + throw new \RuntimeException("Unknown comparison operator: " . $comparison->getOperator()); + } + } +} diff --git a/system/src/Grav/Framework/Object/Interfaces/ObjectCollectionInterface.php b/system/src/Grav/Framework/Object/Interfaces/ObjectCollectionInterface.php index 199cbc3d6b..48879139f5 100644 --- a/system/src/Grav/Framework/Object/Interfaces/ObjectCollectionInterface.php +++ b/system/src/Grav/Framework/Object/Interfaces/ObjectCollectionInterface.php @@ -8,13 +8,14 @@ namespace Grav\Framework\Object\Interfaces; +use Doctrine\Common\Collections\Selectable; use Grav\Framework\Collection\CollectionInterface; /** * ObjectCollection Interface * @package Grav\Framework\Collection */ -interface ObjectCollectionInterface extends CollectionInterface, ObjectInterface +interface ObjectCollectionInterface extends CollectionInterface, Selectable, ObjectInterface { /** * Create a copy from this collection by cloning all objects in the collection. diff --git a/system/src/Grav/Framework/Object/ObjectCollection.php b/system/src/Grav/Framework/Object/ObjectCollection.php index a637bf1e00..1d0ff30ce1 100644 --- a/system/src/Grav/Framework/Object/ObjectCollection.php +++ b/system/src/Grav/Framework/Object/ObjectCollection.php @@ -8,9 +8,11 @@ namespace Grav\Framework\Object; +use Doctrine\Common\Collections\Criteria; use Grav\Framework\Collection\ArrayCollection; use Grav\Framework\Object\Access\NestedPropertyCollectionTrait; use Grav\Framework\Object\Base\ObjectCollectionTrait; +use Grav\Framework\Object\Collection\ObjectExpressionVisitor; use Grav\Framework\Object\Interfaces\NestedObjectInterface; use Grav\Framework\Object\Interfaces\ObjectCollectionInterface; @@ -36,6 +38,39 @@ public function __construct(array $elements = [], $key = null) $this->setKey($key); } + /** + * {@inheritDoc} + */ + public function matching(Criteria $criteria) + { + $expr = $criteria->getWhereExpression(); + $filtered = $this->getElements(); + + if ($expr) { + $visitor = new ObjectExpressionVisitor(); + $filter = $visitor->dispatch($expr); + $filtered = array_filter($filtered, $filter); + } + + if ($orderings = $criteria->getOrderings()) { + $next = null; + foreach (array_reverse($orderings) as $field => $ordering) { + $next = ObjectExpressionVisitor::sortByField($field, $ordering == Criteria::DESC ? -1 : 1, $next); + } + + uasort($filtered, $next); + } + + $offset = $criteria->getFirstResult(); + $length = $criteria->getMaxResults(); + + if ($offset || $length) { + $filtered = array_slice($filtered, (int)$offset, $length); + } + + return $this->createFrom($filtered); + } + protected function getElements() { return $this->toArray(); diff --git a/system/src/Grav/Framework/Object/Property/ObjectPropertyTrait.php b/system/src/Grav/Framework/Object/Property/ObjectPropertyTrait.php index c26903f216..e330a330cc 100644 --- a/system/src/Grav/Framework/Object/Property/ObjectPropertyTrait.php +++ b/system/src/Grav/Framework/Object/Property/ObjectPropertyTrait.php @@ -95,10 +95,10 @@ protected function doHasProperty($property) } /** - * @param string $property Object property to be fetched. - * @param mixed $default Default value if property has not been set. - * @param bool $doCreate Set true to create variable. - * @return mixed Property value. + * @param string $property Object property to be fetched. + * @param mixed $default Default value if property has not been set. + * @param callable|bool $doCreate Set true to create variable. + * @return mixed Property value. */ protected function &doGetProperty($property, $default = null, $doCreate = false) { diff --git a/system/src/Grav/Framework/Route/Route.php b/system/src/Grav/Framework/Route/Route.php index 503d2a8ad5..63f6f7ec3c 100644 --- a/system/src/Grav/Framework/Route/Route.php +++ b/system/src/Grav/Framework/Route/Route.php @@ -178,7 +178,7 @@ public function getQueryParam($param) */ public function withGravParam($param, $value) { - return $this->withParam('gravParams', $param, $value); + return $this->withParam('gravParams', $param, null !== $value ? (string)$value : null); } /** @@ -222,17 +222,16 @@ public function __toString() protected function withParam($type, $param, $value) { $oldValue = isset($this->{$type}[$param]) ? $this->{$type}[$param] : null; - $newValue = null !== $value ? (string)$value : null; - if ($oldValue === $newValue) { + if ($oldValue === $value) { return $this; } $new = clone $this; - if ($newValue === null) { + if ($value === null) { unset($new->{$type}[$param]); } else { - $new->{$type}[$param] = $newValue; + $new->{$type}[$param] = $value; } return $new; diff --git a/system/src/Grav/Framework/Route/RouteFactory.php b/system/src/Grav/Framework/Route/RouteFactory.php index a406926cf7..830d7c75c4 100644 --- a/system/src/Grav/Framework/Route/RouteFactory.php +++ b/system/src/Grav/Framework/Route/RouteFactory.php @@ -28,6 +28,23 @@ public static function createFromParts($parts) return new Route($parts); } + public static function createFromString($path) + { + $path = ltrim($path, '/'); + $parts = [ + 'path' => $path, + 'query' => '', + 'query_params' => [], + 'grav' => [ + 'root' => self::$root, + 'language' => self::$language, + 'route' => $path, + 'params' => '' + ], + ]; + return new Route($parts); + } + public static function getRoot() { return self::$root; diff --git a/system/src/Grav/Framework/Session/Session.php b/system/src/Grav/Framework/Session/Session.php new file mode 100644 index 0000000000..3c2a417e12 --- /dev/null +++ b/system/src/Grav/Framework/Session/Session.php @@ -0,0 +1,340 @@ +isSessionStarted()) { + session_unset(); + session_destroy(); + } + + // Set default options. + $options += array( + 'cache_limiter' => 'nocache', + 'use_trans_sid' => 0, + 'use_cookies' => 1, + 'lazy_write' => 1, + 'use_strict_mode' => 1 + ); + + $this->setOptions($options); + + session_register_shutdown(); + + self::$instance = $this; + } + + /** + * @inheritdoc + */ + public function getId() + { + return session_id(); + } + + /** + * @inheritdoc + */ + public function setId($id) + { + session_id($id); + + return $this; + } + + /** + * @inheritdoc + */ + public function getName() + { + return session_name(); + } + + /** + * @inheritdoc + */ + public function setName($name) + { + session_name($name); + + return $this; + } + + /** + * @inheritdoc + */ + public function setOptions(array $options) + { + if (headers_sent() || \PHP_SESSION_ACTIVE === session_status()) { + return; + } + + $allowedOptions = [ + 'save_path' => true, + 'name' => true, + 'save_handler' => true, + 'gc_probability' => true, + 'gc_divisor' => true, + 'gc_maxlifetime' => true, + 'serialize_handler' => true, + 'cookie_lifetime' => true, + 'cookie_path' => true, + 'cookie_domain' => true, + 'cookie_secure' => true, + 'cookie_httponly' => true, + 'use_strict_mode' => true, + 'use_cookies' => true, + 'use_only_cookies' => true, + 'referer_check' => true, + 'cache_limiter' => true, + 'cache_expire' => true, + 'use_trans_sid' => true, + 'trans_sid_tags' => true, // PHP 7.1 + 'trans_sid_hosts' => true, // PHP 7.1 + 'sid_length' => true, // PHP 7.1 + 'sid_bits_per_character' => true, // PHP 7.1 + 'upload_progress.enabled' => true, + 'upload_progress.cleanup' => true, + 'upload_progress.prefix' => true, + 'upload_progress.name' => true, + 'upload_progress.freq' => true, + 'upload_progress.min-freq' => true, + 'lazy_write' => true, + 'url_rewriter.tags' => true, // Not used in PHP 7.1 + 'hash_function' => true, // Not used in PHP 7.1 + 'hash_bits_per_character' => true, // Not used in PHP 7.1 + 'entropy_file' => true, // Not used in PHP 7.1 + 'entropy_length' => true, // Not used in PHP 7.1 + ]; + + foreach ($options as $key => $value) { + if (is_array($value)) { + // Allow nested options. + foreach ($value as $key2 => $value2) { + $ckey = "{$key}.{$key2}"; + if (isset($value2, $allowedOptions[$ckey])) { + $this->ini_set("session.{$ckey}", $value2); + } + } + } elseif (isset($value, $allowedOptions[$key])) { + $this->ini_set("session.{$key}", $value); + } + } + } + + /** + * @inheritdoc + */ + public function start($readonly = false) + { + // Protection against invalid session cookie names throwing exception: http://php.net/manual/en/function.session-id.php#116836 + if (isset($_COOKIE[session_name()]) && !preg_match('/^[-,a-zA-Z0-9]{1,128}$/', $_COOKIE[session_name()])) { + unset($_COOKIE[session_name()]); + } + + $options = $readonly ? ['read_and_close' => '1'] : []; + + $success = @session_start($options); + if (!$success) { + $last = error_get_last(); + $error = $last ? $last['message'] : 'Unknown error'; + throw new \RuntimeException('Failed to start session: ' . $error, 500); + } + + $params = session_get_cookie_params(); + + setcookie( + session_name(), + session_id(), + time() + $params['lifetime'], + $params['path'], + $params['domain'], + $params['secure'], + $params['httponly'] + ); + + $this->started = true; + + return $this; + } + + /** + * @inheritdoc + */ + public function invalidate() + { + $params = session_get_cookie_params(); + setcookie( + session_name(), + '', + time() - 42000, + $params['path'], + $params['domain'], + $params['secure'], + $params['httponly'] + ); + + session_unset(); + session_destroy(); + + $this->started = false; + + return $this; + } + + /** + * @inheritdoc + */ + public function close() + { + if ($this->started) { + session_write_close(); + } + + $this->started = false; + + return $this; + } + + /** + * @inheritdoc + */ + public function clear() + { + session_unset(); + + return $this; + } + + /** + * @inheritdoc + */ + public function getAll() + { + return $_SESSION; + } + + /** + * @inheritdoc + */ + public function getIterator() + { + return new \ArrayIterator($_SESSION); + } + + /** + * @inheritdoc + */ + public function isStarted() + { + return $this->started; + } + + /** + * @inheritdoc + */ + public function __isset($name) + { + return isset($_SESSION[$name]); + } + + /** + * @inheritdoc + */ + public function __get($name) + { + return isset($_SESSION[$name]) ? $_SESSION[$name] : null; + } + + /** + * @inheritdoc + */ + public function __set($name, $value) + { + $_SESSION[$name] = $value; + } + + /** + * @inheritdoc + */ + public function __unset($name) + { + unset($_SESSION[$name]); + } + + /** + * http://php.net/manual/en/function.session-status.php#113468 + * Check if session is started nicely. + * @return bool + */ + protected function isSessionStarted() + { + return \PHP_SAPI !== 'cli' ? \PHP_SESSION_ACTIVE === session_status() : false; + } + + /** + * @param string $key + * @param mixed $value + */ + protected function ini_set($key, $value) + { + if (!is_string($value)) { + if (is_bool($value)) { + $value = $value ? '1' : '0'; + } + $value = (string)$value; + } + + ini_set($key, $value); + } +} diff --git a/system/src/Grav/Framework/Session/SessionInterface.php b/system/src/Grav/Framework/Session/SessionInterface.php new file mode 100644 index 0000000000..74c5f2aca4 --- /dev/null +++ b/system/src/Grav/Framework/Session/SessionInterface.php @@ -0,0 +1,147 @@ +assertEquals('engli' . '...', Utils::truncate('english', 5, true, " ", "...")); $this->assertEquals('english', Utils::truncate('english')); $this->assertEquals('This is a string to truncate', Utils::truncate('This is a string to truncate')); - $this->assertEquals('This ', Utils::truncate('This is a string to truncate', 3, true)); - $this->assertEquals('', 6, true)); + $this->assertEquals('This' . '…', Utils::truncate('This is a string to truncate', 3, true)); + $this->assertEquals('', 6, true)); } public function testSafeTruncate() { - $this->assertEquals('This ', Utils::safeTruncate('This is a string to truncate', 1)); - $this->assertEquals('This ', Utils::safeTruncate('This is a string to truncate', 4)); - $this->assertEquals('This is ', Utils::safeTruncate('This is a string to truncate', 5)); + $this->assertEquals('This' . '…', Utils::safeTruncate('This is a string to truncate', 1)); + $this->assertEquals('This' . '…', Utils::safeTruncate('This is a string to truncate', 4)); + $this->assertEquals('This is' . '…', Utils::safeTruncate('This is a string to truncate', 5)); } public function testTruncateHtml() diff --git a/user/pages/02.typography/default.md b/user/pages/02.typography/default.md index 88691f6bbc..d3aa848509 100644 --- a/user/pages/02.typography/default.md +++ b/user/pages/02.typography/default.md @@ -53,7 +53,7 @@ _Italic_ `_Italic_` TextSuperscripted `` -TextSubscxripted `` +TextSubscripted `` Underlined `` diff --git a/webserver-configs/nginx-ddev-site.conf b/webserver-configs/nginx-ddev-site.conf new file mode 100644 index 0000000000..e75cf2ff11 --- /dev/null +++ b/webserver-configs/nginx-ddev-site.conf @@ -0,0 +1,118 @@ +# ddev GravCMS config + +# You can override ddev's configuration by placing an edited copy +# of this config (or one of the other ones) in .ddev/nginx-site.conf +# See https://ddev.readthedocs.io/en/latest/users/extend/customization-extendibility/#providing-custom-nginx-configuration + +# Set https to 'on' if x-forwarded-proto is https +map $http_x_forwarded_proto $fcgi_https { + default off; + https on; +} + +server { + listen 80; ## listen for ipv4; this line is default and implied + listen [::]:80 default ipv6only=on; ## listen for ipv6 + # The NGINX_DOCROOT variable is substituted with + # its value when the container is started. + root $NGINX_DOCROOT; + index index.php index.htm index.html; + + # Make site accessible from http://localhost/ + server_name _; + + # Disable sendfile as per https://docs.vagrantup.com/v2/synced-folders/virtualbox.html + sendfile off; + error_log /var/log/nginx/error.log info; + access_log /var/log/nginx/access.log; + + location / { + absolute_redirect off; + try_files $uri $uri/ /index.php?$query_string; + } + + # pass the PHP scripts to FastCGI server listening on socket + location ~ \.php$ { + try_files $uri =404; + fastcgi_split_path_info ^(.+\.php)(/.+)$; + fastcgi_pass unix:/run/php-fpm.sock; + fastcgi_buffers 16 16k; + fastcgi_buffer_size 32k; + fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; + fastcgi_param SCRIPT_NAME $fastcgi_script_name; + fastcgi_index index.php; + include fastcgi_params; + fastcgi_intercept_errors on; + # fastcgi_read_timeout should match max_execution_time in php.ini + fastcgi_read_timeout 10m; + fastcgi_param SERVER_NAME $host; + fastcgi_param HTTPS $fcgi_https; + } + + # Expire rules for static content + # Feed + location ~* \.(?:rss|atom|cache)$ { + expires 1h; + } + + # Media: images, icons, video, audio, HTC + location ~* \.(?:jpg|jpeg|gif|png|ico|cur|gz|svg|svgz|mp4|ogg|ogv|webm|htc)$ { + expires 1M; + access_log off; + add_header Cache-Control "public"; + } + + # Prevent clients from accessing hidden files (starting with a dot) + # This is particularly important if you store .htpasswd files in the site hierarchy + # Access to `/.well-known/` is allowed. + # https://www.mnot.net/blog/2010/04/07/well-known + # https://tools.ietf.org/html/rfc5785 + location ~* /\.(?!well-known\/) { + deny all; + } + + # Prevent clients from accessing to backup/config/source files + location ~* (?:\.(?:bak|conf|dist|fla|in[ci]|log|psd|sh|sql|sw[op])|~)$ { + deny all; + } + + ## Begin - Security + # deny all direct access for these folders + location ~* /(\.git|cache|bin|logs|backup|tests)/.*$ { return 403; } + # deny running scripts inside core system folders + location ~* /(system|vendor)/.*\.(txt|xml|md|html|yaml|yml|php|pl|py|cgi|twig|sh|bat)$ { return 403; } + # deny running scripts inside user folder + location ~* /user/.*\.(txt|md|yaml|yml|php|pl|py|cgi|twig|sh|bat)$ { return 403; } + # deny access to specific files in the root folder + location ~ /(LICENSE\.txt|composer\.lock|composer\.json|nginx\.conf|web\.config|htaccess\.txt|\.htaccess) { return 403; } + ## End - Security + + + ## provide a health check endpoint + location /healthcheck { + access_log off; + stub_status on; + keepalive_timeout 0; # Disable HTTP keepalive + return 200; + } + + error_page 400 401 /40x.html; + location = /40x.html { + root /usr/share/nginx/html; + } + + location ~ ^/(fpmstatus|ping)$ { + access_log off; + stub_status on; + keepalive_timeout 0; # Disable HTTP keepalive + allow 127.0.0.1; + allow all; + fastcgi_index index.php; + fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; + include fastcgi_params; + fastcgi_pass unix:/run/php-fpm.sock; + } + + +} + diff --git a/webserver-configs/nginx.conf b/webserver-configs/nginx.conf index 89dd1051e4..bbca7d32db 100644 --- a/webserver-configs/nginx.conf +++ b/webserver-configs/nginx.conf @@ -30,7 +30,7 @@ server { ## Begin - PHP location ~ \.php$ { # Choose either a socket or TCP/IP address - fastcgi_pass unix:/var/run/php/php7.0-fpm.sock; + fastcgi_pass unix:/var/run/php/php7.2-fpm.sock; # fastcgi_pass unix:/var/run/php5-fpm.sock; #legacy # fastcgi_pass 127.0.0.1:9000;