From 8b4e3fe7612ac01e7717ef0decd31888efc03403 Mon Sep 17 00:00:00 2001 From: Andy Miller Date: Fri, 9 Mar 2018 11:34:22 -0700 Subject: [PATCH] Squashed commit of the following: MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit d008376169a60ebd74b54ed5d44568f12b96778a Author: Andy Miller Date: Thu Mar 8 17:08:20 2018 -0700 Updated parsedown + toolbox commit eaac77881f0618845bb88d63e24838e1243aea31 Author: Andy Miller Date: Thu Mar 8 14:52:49 2018 -0700 Force clearing PHP `clearstatcache` and `opcache-reset` on `Cache::clear()` commit 43ca0a2f582f27026b7687b5fad26b4071d6ad53 Author: Andy Miller Date: Wed Mar 7 11:57:55 2018 -0700 vendor updates commit 54cd7f85b8457c28de93dce65b948c5cd1657c07 Author: Andy Miller Date: Fri Mar 2 15:39:59 2018 -0600 `theme_var()` twig function now checks page header var first commit 5ffe32ef589b9fb910ca75999a22680f060e09a6 Author: Andy Miller Date: Thu Mar 1 15:30:51 2018 -0600 Fix alt tag issue #1883 commit b0ad83ebf7f954fe876043952ff7fe8ea1e116ab Author: Andy Miller Date: Thu Mar 1 15:13:42 2018 -0600 support both alt and alt_text commit 5b291f8f541b14fac537fe962c5153e8c857e248 Author: Andy Miller Date: Thu Feb 22 16:14:24 2018 -0700 Added Route overrides commit 274c70190778f775a0359dec5a57aafdd8186bb3 Author: Matias Griese Date: Thu Feb 22 09:00:41 2018 +0200 Improved Object serialize()/unserialize() commit e533024b2e0c765b5d0375fb96eacbd40999ece1 Author: Andy Miller Date: Wed Feb 21 23:00:37 2018 -0700 Added `{% switch %}` twig tag commit 8750602f76642e493398cc08822003891a6e474c Author: Andy Miller Date: Wed Feb 21 17:28:43 2018 -0700 Added new `{% markdown %}` twig tag commit 42d883656982ef381584f21ac198ea249d0bc956 Author: Matias Griese Date: Wed Feb 21 19:42:09 2018 +0200 Improved `Session` initialization (#1879) commit 7cfc6fbdb84c5336a1c54f6c4d96f9e96634b87f Merge: 01886b6d b7d4697d Author: Matias Griese Date: Wed Feb 21 12:53:48 2018 +0200 Merge remote-tracking branch 'origin/1.4' into 1.4 commit 01886b6df9c404d571a646eba721a8052fd7dcb0 Author: Matias Griese Date: Wed Feb 21 12:53:40 2018 +0200 Added better session checks, warn in debugbar if session messages cannot be used commit b7d4697db8037fc5fb964ba4f0ec865930efff10 Author: Andy Miller Date: Tue Feb 20 17:30:17 2018 -0700 changelog updated commit 742c6f9baabb9e9d992fe3888650ae6311c9d83b Author: Matias Griese Date: Tue Feb 20 12:00:17 2018 +0200 Fixed issue with UriFactory::parseQuery() commit c97da77153b7f2b8356aefe36a06ea95d75a1542 Merge: 871848dc 84a0b5d1 Author: Matias Griese Date: Tue Feb 20 11:07:36 2018 +0200 Merge branch '1.4' of https://github.com/getgrav/grav into 1.4 commit 871848dc2851c0591ae87e7a61355ed24bf1acd0 Author: Matias Griese Date: Tue Feb 20 11:07:28 2018 +0200 Added `Grav\Framework\Route` classes to allow route/link manipulation Added `$grav['uri]->getCurrentRoute()` method to get `Grav\Framework\Route\Route` instance for the current URL commit 84a0b5d1e1e1b79df935cc62d29fdf03a0866926 Author: Andy Miller Date: Mon Feb 19 21:23:11 2018 -0700 cleanup with default value commit 8e68317e7cb5f432a6499835050ea836129587ab Author: Andy Miller Date: Mon Feb 19 11:48:38 2018 -0700 Composer updates commit 35f7a2a9de1bfb4e559a7392049d39b313b37a60 Author: Matias Griese Date: Mon Feb 19 11:10:37 2018 +0200 Added `$grav['uri]->getCurrentUri()` function to get `Grav\Framework\Uri` instance for the current URL. commit 62dfa0e0609418d0027379bf12625b0449cd1318 Author: Matias Griese Date: Mon Feb 19 10:30:16 2018 +0200 Reorder functions in Common\Uri class commit 2e2846143505d234acbdfc37c920b3c64c77f985 Author: Andy Miller Date: Sun Feb 18 14:08:23 2018 -0700 Updated changelog commit 4259d01638e05f1fa387ad75d7d163b97278ad8b Author: Andy Miller Date: Sun Feb 18 14:07:35 2018 -0700 Fix bug with remote minimum PHP version determination commit 39783df92d124121a727d2ba42a733f29039710f Author: Matias Griese Date: Sat Feb 17 14:35:02 2018 +0200 Fixed bug with absolute_urls=true commit d39253e36fb5c385fe2eb69ea95b63cc64b75d53 Author: Matias Griese Date: Sat Feb 17 14:28:26 2018 +0200 Moved URI parts filtering functions to Framework\UriPartsFilter class commit 82868037ec47344d038b97c01b116718cb2fc42f Author: Matias Griese Date: Sat Feb 17 13:03:56 2018 +0200 Some code cleanup on ParsedownGravTrait and Grav classes commit 17ba58a5c27f77bac01e47ede89d0544907b2c21 Author: Matias Griese Date: Sat Feb 17 13:00:40 2018 +0200 Framework\Uri: Improve query parameter handling commit 0a79788582fd6273081cbc21645b8a430e2b65b7 Author: Matias Griese Date: Sat Feb 17 12:23:45 2018 +0200 Improve Uri class by adding useful tests commit ccf0f9c74e35ab8d114006f47920dd7729c3f4c8 Author: Andy Miller Date: Fri Feb 16 18:16:07 2018 -0700 Added support for `php` dependencies in GPM blueprints commit 79e580a79d0cc774236f3f19b1c34be84e4bc9b8 Author: Matias Griese Date: Sat Feb 17 00:18:21 2018 +0200 Added `Grav\Framework\Uri` classes extending PSR-7 `HTTP message UriInterface` implementation commit 5b9a40de5f05068607c3751ad879496423facbfa Author: Andy Miller Date: Thu Feb 15 15:53:36 2018 -0700 Prepare for release commit 550468aefb8418cb3e0ae1b7344e6da4f885de7f Author: Andy Miller Date: Thu Feb 15 14:51:31 2018 -0700 Added checkbox-style support for `switch` field commit 84a2a08d22581b77766e316ebf7e60da8be3f92e Author: Andy Miller Date: Wed Feb 14 22:29:41 2018 -0700 Fixed YAML blueprint valdiation/filtering commit ee8ea5af570af11af525f3a4374cfc78b2be2e98 Author: Matias Griese Date: Fri Feb 9 10:44:32 2018 +0200 Clean up Utils class with minor fixes commit f91678df4a67744fd4003c5c4884abb012a3a1d7 Author: Andy Miller Date: Tue Feb 6 09:42:13 2018 -0700 Fix for invalid template error in evaluate() commit 4c654ef19c3ec009c179fca42d02739388157ee3 Author: Andy Miller Date: Mon Feb 5 13:07:51 2018 -0700 Stopped Chrome from auto-completing admin user profile form #1847 commit 637308d29448680a5e47528411af440dbd653310 Author: Andy Miller Date: Mon Feb 5 11:59:14 2018 -0700 Fix for opengraph tags #1849 commit 08be06c9bc86344713d198149249d7279c98ad20 Author: Andy Miller Date: Wed Jan 31 16:07:36 2018 -0700 Addd a new Medium:exists() method commit 19ae66d0e9f1278fd5911fbcf5027d580b4ae945 Author: Andy Miller Date: Wed Jan 31 14:04:56 2018 -0700 updated changelog commit c3f84d868200ed6dc39c484843625ec88f1b1d0f Author: Andy Miller Date: Wed Jan 31 14:04:50 2018 -0700 new read_file() twig function commit c721be8787b09aab1dce6bd012c8d43d1a985558 Author: Andy Miller Date: Thu Jan 25 16:40:02 2018 -0700 Added yaml encode/decode filters commit 47746d3313b3198c6217dde3bf9e8f61c37e35d1 Author: Andy Miller Date: Thu Jan 25 16:37:21 2018 -0700 Added new Collection::toExtendedArray() commit 103ac4b1378e2191ae63cdfdeb2003fb9169f3d0 Author: Andy Miller Date: Wed Jan 24 15:42:27 2018 -0700 Improve collection filtering commit d2e3ea0f9d5833156f37e6fe17ef9bb32001de7d Author: Andy Miller Date: Mon Jan 22 12:27:03 2018 -0700 Prepare for release commit 16ca3daf2166ffadee99b248312a6c12fb52c99f Author: Andy Miller Date: Mon Jan 22 12:25:17 2018 -0700 Updated pimple release commit db5f0efaab34c697408cb79ced095fc61e9fea31 Author: Andy Miller Date: Sun Jan 21 17:01:45 2018 -0700 Fix for bad `Uri::filter()` call https://github.com/getgrav/grav-plugin-page-inject/issues/14 commit 8eb0f8770238a54a5cc4a3ae1e257d79e3aebedf Author: Andy Miller Date: Fri Jan 19 11:19:29 2018 -0700 Changelog updated commit 2c44980b77b3aae271162ba14ca930644c6b465f Author: Gabriel Caruso Date: Fri Jan 19 16:15:20 2018 -0200 Refactoring tests (#1779) commit 75e293b2d7f6e3c010424a9811d83c827df2975b Merge: 422eb5be 821c4f7b Author: Andy Miller Date: Fri Jan 19 11:13:49 2018 -0700 Merge branch '1.4' of https://github.com/getgrav/grav into 1.4 commit 422eb5be5cebdc5b7b5a27769e152983e37005ed Author: Andy Miller Date: Fri Jan 19 11:13:27 2018 -0700 License date update commit 821c4f7b4a6686d8def812e4a467a24cf6f0dcc0 Author: Matias Griese Date: Fri Jan 19 20:09:51 2018 +0200 Clean up Utils class with minor fixes (#1830) commit dcbb5ef90df11c4be34379c6113751cff81c7987 Merge: 758ea8f5 8d39fdf2 Author: Andy Miller Date: Fri Jan 19 10:32:37 2018 -0700 Merge branch '1.4' of https://github.com/getgrav/grav into 1.4 # Conflicts: # CHANGELOG.md commit 758ea8f581c551892839664181c82bc42e3fcbc7 Author: Andy Miller Date: Fri Jan 19 10:31:33 2018 -0700 Slight change in Whoops colors commit 5a279774520244d0eb2f7b9d1bba2ae9b350bb8d Author: Andy Miller Date: Fri Jan 19 10:30:53 2018 -0700 vendor updates commit 8d39fdf23cde2a8154f739edf7b39567e9af0b4c Author: Matias Griese Date: Fri Jan 19 12:53:33 2018 +0200 Bug fix on Cache TTL = null commit 73bd402551aff64fc528e2e0ea7166b320965107 Author: Matias Griese Date: Fri Jan 19 12:03:32 2018 +0200 `Framework\Cache`: Update all exceptions to be compatible with `Psr\SimpleCache` commit c473c0baafa69fbb95af1fbfa003d4a05bb9b1b2 Author: Matias Griese Date: Fri Jan 19 11:49:01 2018 +0200 Fixes wrong exception in ChainCache commit 827b4e5d756363cf666b42f4f6e8ff4bc13acba9 Author: Matias Griese Date: Fri Jan 19 11:28:47 2018 +0200 Clean up Utils class with minor fixes commit 14af38fb0f64f32e5d1754cb192d2fc346ba0b3a Author: Andy Miller Date: Thu Jan 11 17:52:10 2018 -0700 Moved url() function into Utils class commit 2c4d5307f67130b773e00bc943ea503afa7b4b28 Merge: 4677bad9 ae245c06 Author: Andy Miller Date: Tue Jan 9 22:47:15 2018 -0700 Merge branch '1.4' of https://github.com/getgrav/grav into 1.4 commit 4677bad9ba76e1a802772f85a03eaacaa59e0686 Author: Andy Miller Date: Tue Jan 9 22:47:11 2018 -0700 Modular blueprint update commit ae245c0674c8c2c6355ecf94305abb5b65e13806 Author: Matias Griese Date: Tue Jan 9 21:24:19 2018 +0200 Misc object fixes (new features only) commit 154fa4ed9ad09b73490b8120ba9d88c64b2e5d93 Author: Matias Griese Date: Tue Jan 9 13:02:36 2018 +0200 Update changelog commit a744b7fc8f09d13cb5a4f16ee0d96a5c66da1022 Author: Matias Griese Date: Tue Jan 9 12:08:37 2018 +0200 Objects: Add protected function `getVariable()` to get serialized value for a single property `ObjectPropertyTrait`: Added protected functions `isPropertyLoaded()`, `offsetLoad()`, `offsetPrepare()` and `offsetSerialize()` commit 72abab645055c715f25c420bc3f6ff91d9c96dc3 Author: Andy Miller Date: Thu Jan 4 11:45:31 2018 -0700 Dates should always be sorted numerically commit 0feeb743fffbbeac23aa4a7076cfa3d0b2b91f49 Author: Andy Miller Date: Fri Dec 29 13:24:10 2017 -0700 update defines commit 96fd694eaeb2250c9b32b01dc6930d09dc6b3f1d Author: Andy Miller Date: Fri Dec 29 13:12:18 2017 -0700 Use quark by default commit b2c883c8699349c8b8e5f7292c9bab0e91585a41 Author: Andy Miller Date: Fri Dec 29 13:11:12 2017 -0700 updated changelog commit 7941786db9d1adac8bf5962cfdd5ef3fe12823e7 Author: Andy Miller Date: Thu Dec 28 17:21:52 2017 -0700 Setting Quark to default theme commit 2ad9b6dc84999b7c7b7860179e4ece9658e79366 Author: Andy Miller Date: Thu Dec 28 14:39:35 2017 -0700 Fixes that ‘should’ work. commit 59f7f1543472f5bfd68e499bbdaccf5993a87b65 Author: Andy Miller Date: Wed Dec 27 14:58:56 2017 -0700 Get current route correctly commit 8f522ee11ddd8dbe21f43c97f33fa07107573254 Merge: f48c0b17 b35b9eae Author: Andy Miller Date: Wed Dec 27 13:39:27 2017 -0700 Merge branch '1.4' of https://github.com/getgrav/grav into 1.4 commit f48c0b1700e70ce6457f5d72cc02e031ca7cd3d5 Author: Andy Miller Date: Wed Dec 27 13:39:16 2017 -0700 fix for special chars in base path #1799 commit b35b9eaecc253581bf29f46c0ea0e7ecfc7e3d74 Merge: e217489d 5df6db9f Author: Matias Griese Date: Wed Dec 27 21:03:48 2017 +0200 Merge remote-tracking branch 'origin/1.4' into 1.4 commit e217489d37b917f9a7931fd551498cdd88eae9e5 Author: Matias Griese Date: Wed Dec 27 21:03:41 2017 +0200 Added new configuration option `system.session.initialize` to delay session initialization if needed by a plugin commit 5df6db9ffa26341ee00c501655662a56100a4f93 Author: Andy Miller Date: Tue Dec 26 15:01:44 2017 -0700 Added a new typography page commit bd578070de7e29f16bd8853d6d499081f8cb1074 Author: Andy Miller Date: Mon Dec 18 17:34:49 2017 -0700 removed _set method as it breaks GPM commit a8a061d4b292ba214dddd5cbb6dbbdf61e7786c0 Author: Andy Miller Date: Mon Dec 18 14:28:07 2017 -0700 updated defines for beta.2 commit 9d41417c263de747b5982ffff8b5abed46f16eee Author: Andy Miller Date: Mon Dec 18 14:21:43 2017 -0700 updated changelog commit 3888358dc971ca9419734310e97f062e31d5ad2f Author: Andy Miller Date: Mon Dec 18 14:07:55 2017 -0700 Updated to latest vendor libs commit 62302c0420a2b94997d82b89a68f2417959bf0c6 Merge: 2d93c1ac 42de0bbb Author: Andy Miller Date: Mon Dec 18 12:18:17 2017 -0700 Merge branch '1.4' of https://github.com/getgrav/grav into 1.4 commit 2d93c1ace09e6198c070a6af5424609302912226 Author: Andy Miller Date: Mon Dec 18 12:18:10 2017 -0700 title change commit 42de0bbb32b22d9f291fdada1004766090844399 Author: Matias Griese Date: Mon Dec 18 09:16:52 2017 +0200 Changelog update commit 3e15de74f4d913a9ef584acc81c1084b7dc454e6 Author: Andy Miller Date: Sun Dec 17 13:45:48 2017 -0700 Added `header_var` twig function commit a1c804ed41d08ba94e0cbf3072ef27e31ae5317e Author: Andy Miller Date: Fri Dec 15 16:08:53 2017 -0700 Changed title commit 559fde4e00cb5a5bce39112c7f2b58c5e3a1b2d2 Author: Andy Miller Date: Fri Dec 15 15:54:50 2017 -0700 Tweak to default page commit 1f1c429fc4089bfd710704f8a81654bd47997060 Author: Andy Miller Date: Fri Dec 15 15:27:23 2017 -0700 updated changelog commit e9b7c8dad294124ac75b2180bb2b37161d46ff29 Merge: b6e5bfa7 fb106b11 Author: Andy Miller Date: Fri Dec 15 15:13:29 2017 -0700 Merge branch '1.4' of https://github.com/getgrav/grav into 1.4 # Conflicts: # CHANGELOG.md commit b6e5bfa7f34422c032679bc3be7b67d92ec9aa6c Author: Andy Miller Date: Fri Dec 15 15:12:26 2017 -0700 Fixed `BadMethodException` in GPM updates #1784 commit fb106b1106f6845857fe25ae3a06d2ec73edaaeb Author: Matias Griese Date: Fri Dec 15 21:31:40 2017 +0200 Removed constructor from ObjectInterface commit 34e3be710f9243440433e74ee2703720e853615c Merge: 21674194 af146527 Author: Matias Griese Date: Fri Dec 15 21:28:51 2017 +0200 Merge branch 'develop' of https://github.com/getgrav/grav into 1.4 # Conflicts: # composer.lock commit 21674194e0501b74884e6bdf6c35a6682871d8ad Author: Andy Miller Date: Wed Dec 13 22:19:43 2017 -0700 Updated changelog commit aadb8bb2eabd50c816e72c9c1439eaa849485571 Author: Andy Miller Date: Wed Dec 13 21:51:06 2017 -0700 Added some new theme twig functions commit 592c3ea5e2125d008aaadecc8152f709022133fa Merge: 97b088b9 3fec18e5 Author: Andy Miller Date: Tue Dec 12 11:29:51 2017 -0700 Merge branch '1.4' of https://github.com/getgrav/grav into 1.4 commit 97b088b959301f3bb0282647a8e0e4094f71c3a2 Author: Andy Miller Date: Tue Dec 12 11:29:02 2017 -0700 Updated welcome page commit 3fec18e5e58022f1bb089a92bfed44b45244f8e3 Author: Matias Griese Date: Tue Dec 12 12:47:12 2017 +0200 Composer update commit b15c5d7888fd019378eba0870495e9aec559a79a Merge: d629391a 24898de2 Author: Matias Griese Date: Tue Dec 12 12:45:14 2017 +0200 Merge branch 'develop' of https://github.com/getgrav/grav into 1.4 commit d629391ad940073304ab4ce56ae47aecf06b3dd7 Author: Andy Miller Date: Mon Dec 11 15:08:20 2017 -0700 updated changelog commit 7ec66b2bf296689a7accd38ee5626d01b32b0070 Author: Andy Miller Date: Mon Dec 11 15:08:05 2017 -0700 Transferred copyright commit b5f75acf50122ed90ec014e86d7a0a8868788ef5 Author: Andy Miller Date: Mon Dec 11 13:17:03 2017 -0700 Fix array casting with DOM elements commit 255f2f1db6e76e33e445788c3ade65995daa4209 Author: Djamil Legato Date: Mon Dec 11 11:56:07 2017 -0800 Changed GPM channel to testing commit d34fad65b5667f2697a8a2a25d4bd27d32ca07b2 Author: Djamil Legato Date: Mon Dec 11 11:49:19 2017 -0800 Removed Page interfaces commit 8d82fce89f437aacd9dfbc61165147e1a4493c59 Author: Djamil Legato Date: Mon Dec 11 11:46:30 2017 -0800 Updated version to 1.4.0-beta.1 commit bfd8a087bf775099a7d1b5f399c7cbb857bb76c9 Merge: cc648b88 6628bbbe Author: Matias Griese Date: Mon Dec 11 20:54:33 2017 +0200 Merge branch 'develop' of https://github.com/getgrav/grav into 2.0 # Conflicts: # CHANGELOG.md # system/defines.php commit cc648b885cfc959d788246c4889508bb015cc318 Author: Djamil Legato Date: Fri Dec 8 18:14:04 2017 -0800 Updated grav version for 2.0 branch commit acbc85773c36031f42ef5ba85d7271c3a6ab7487 Merge: d943c559 b2fc1010 Author: Matias Griese Date: Fri Dec 1 20:41:25 2017 +0200 Merge remote-tracking branch 'origin/2.0' into 2.0 commit b2fc101078bdd08d2c4aa1863060a3aa72e9a920 Merge: 0524bd20 6ae4680f Author: Djamil Legato Date: Fri Dec 1 10:41:06 2017 -0800 Merge branch 'develop' into 2.0 commit d943c559e2f10e9573dcb138178a9cfd147d4332 Merge: 0524bd20 6ae4680f Author: Matias Griese Date: Fri Dec 1 20:40:36 2017 +0200 Merge branch 'develop' of https://github.com/getgrav/grav into 2.0 commit 0524bd2083244194ba5b10bb75383042517d6d75 Merge: 66d8269a b4cf7899 Author: Matias Griese Date: Fri Dec 1 14:49:26 2017 +0200 Merge branch 'develop' of https://github.com/getgrav/grav into 2.0 commit 66d8269a1b84d6b6392fe0d088fce832c8848b6b Merge: ddd451b9 a1eccfdb Author: Matias Griese Date: Wed Nov 29 10:46:46 2017 +0200 Merge branch 'develop' of https://github.com/getgrav/grav into 2.0 commit ddd451b94c05f2d80c564bc67e3fceb4f16f2518 Merge: aa2341df cb4147a4 Author: Matias Griese Date: Fri Nov 17 08:04:41 2017 +0200 Merge branch 'develop' of https://github.com/getgrav/grav into 2.0 commit aa2341dfb5f3312a199410a38e3966e9916d79fa Merge: b31490e5 31e3c1c2 Author: Matias Griese Date: Mon Nov 13 09:48:35 2017 +0200 Merge branch 'develop' of https://github.com/getgrav/grav into 2.0 # Conflicts: # system/src/Grav/Common/User/User.php commit b31490e52dad2b7c97a6e5803966b87896dc766b Merge: 980b2b67 0fd22ad9 Author: Matias Griese Date: Mon Nov 6 10:28:10 2017 +0200 Merge branch 'develop' of https://github.com/getgrav/grav into 2.0 commit 980b2b67cd1159e113bdffafe1f1f660bbb27e90 Author: Matias Griese Date: Mon Nov 6 10:18:22 2017 +0200 Code readability improvements commit cd15055280a9a5d4eb5e2049f9e828f26c7d396a Merge: 3380577e 0895b154 Author: Matias Griese Date: Wed Nov 1 07:48:45 2017 +0200 Merge branch 'develop' of https://github.com/getgrav/grav into 2.0 # Conflicts: # CHANGELOG.md commit 3380577e4000f373c1fd967ecf6bffb710bdf50f Author: Matias Griese Date: Wed Nov 1 07:45:59 2017 +0200 Improved script/style tag parameter handling commit 7c19d1520df5cb9edfc537726b079ba5248fd25f Author: Matias Griese Date: Tue Oct 31 20:02:26 2017 +0200 Tweaked script/style tag formatting commit 84789cbcd44f82e96cacd557f5f8af77b1077230 Author: Matias Griese Date: Tue Oct 31 16:50:05 2017 +0200 Added `{% script %}` and `{% style %}` tags for Twig templates commit 586fcf42ab7fde975ca01816af56e8ee311bff0f Author: Matias Griese Date: Mon Oct 30 13:31:47 2017 +0200 Added `{% try %} ... {% catch %} Error: {{ e.message }} {% endcatch %}` tag to allow basic exception handling inside Twig commit 18c597b3d2568c50e34c241f567f2f65c8e1f78f Merge: d00d8cb6 e9e4106d Author: Matias Griese Date: Fri Oct 27 11:49:43 2017 +0300 Merge branch 'develop' of https://github.com/getgrav/grav into 2.0 commit d00d8cb6688656a4afc2a389b7a4a5f08e5da790 Merge: ae143814 d3695b2f Author: Matias Griese Date: Wed Oct 25 11:07:39 2017 +0300 Merge branch 'develop' of https://github.com/getgrav/grav into 2.0 # Conflicts: # CHANGELOG.md commit ae143814fb9541e02ab0fdf7f2cff8c7dfb90d9d Author: Djamil Legato Date: Thu Oct 19 17:37:29 2017 -0400 Updated default with new media order commit 98dca95c15089c1bc900aa8f42230f7ae00a0beb Merge: 731fe500 2842b8fa Author: Matias Griese Date: Wed Oct 18 09:16:46 2017 +0300 Merge branch 'develop' of https://github.com/getgrav/grav into 2.0 # Conflicts: # CHANGELOG.md commit 731fe500ee04b64def59749ab2ee42ad94db5d1b Merge: c7be8ebb cc695917 Author: Matias Griese Date: Fri Oct 13 11:41:10 2017 +0300 Merge branch 'develop' of https://github.com/getgrav/grav into 2.0 # Conflicts: # CHANGELOG.md commit c7be8ebb226e38fa70ba97cd23147095243256d6 Merge: 4e6738cb 974fc552 Author: Matias Griese Date: Thu Oct 12 10:50:08 2017 +0300 Merge branch 'develop' of https://github.com/getgrav/grav into 2.0 commit 4e6738cbc5c54e9d857e1467a85f21dd36c4cc5b Author: Matias Griese Date: Wed Oct 4 15:24:45 2017 +0300 Composer update commit 0d4a5a22c8da7c3e0e9911d3f9c7633ead1b55ce Merge: 8b3949b7 15ec8fe3 Author: Matias Griese Date: Wed Oct 4 13:09:31 2017 +0300 Merge branch 'develop' of https://github.com/getgrav/grav into 2.0 # Conflicts: # CHANGELOG.md # composer.lock # system/src/Grav/Common/Twig/TwigExtension.php commit 8b3949b7d924a3ba89a92e7883fe83cd59d32666 Author: Matias Griese Date: Wed Sep 27 10:13:26 2017 +0300 Fixed nested object access by Object::getNestedProperty() commit a535e8547a2365e672eb18b1f4670b8d9d7b8629 Author: Matias Griese Date: Tue Sep 26 19:02:51 2017 +0300 Fix bug commit 0fd6fda4d8ffb29d3462a88ec9333e72534cdf1a Author: Matias Griese Date: Tue Sep 26 18:26:39 2017 +0300 Add method ObjectCollection::collectionGroup() commit 71b11366bbc83cfdb16bfe4478828a7c521ea50a Author: Matias Griese Date: Mon Sep 25 14:17:14 2017 +0300 Add support to serialize Object properties commit 588c5091942e7b62e151710c6db66b2558bb38d6 Author: Matias Griese Date: Fri Sep 15 14:05:21 2017 +0300 Add ArrayCollection::chunk() function commit 37b5818507052000d381990c5aae3898e0139ddc Author: mahagr Date: Thu Sep 14 15:58:16 2017 +0300 Fix bug on Object ArrayAccess commit 23592b8a765381fd5f2ee63bbf808824607bd5cc Author: Matias Griese Date: Wed Sep 13 09:50:34 2017 +0300 Major update on Object classes (backwards incompatible) commit 62389975a2ec6b36ee9f0bcac05a3a5b8dcce9e4 Author: Matias Griese Date: Fri Sep 8 15:50:05 2017 +0300 Object code cleanup commit c0e1e155d75664f8ba3962f293ca5551482c9eaa Merge: 8c321033 f2898f9f Author: Matias Griese Date: Fri Sep 8 14:54:50 2017 +0300 Merge branch 'develop' of https://github.com/getgrav/grav into 2.0 # Conflicts: # composer.json # composer.lock # system/src/Grav/Common/Twig/TwigExtension.php commit 8c3210332e8bfd32db0e12e1aabddb6ff170291d Author: Matias Griese Date: Fri Sep 8 14:44:37 2017 +0300 Add `ArrayObject` and make `Object` not to implement `ArrayAccess` interface. Remove Toolbox dependency. commit 758ccadb07f9628ffc0a371ef01702508d129d18 Author: Matias Griese Date: Fri Sep 8 14:40:16 2017 +0300 Code cleanup commit cd11b149988d69c3997e5f6330b0784f94d25cbe Author: Matias Griese Date: Fri Sep 8 14:39:33 2017 +0300 Add `FileCollectionInterface` and `AbstractFileCollection` classes commit cdb5d591e8ee2c44c3cc960c94ba4e498013de9e Author: Matias Griese Date: Wed Aug 30 14:08:57 2017 +0300 Fix a bug in MemoryCache commit fab81b923bafe43b7f99a92c67e5e42edb50201a Author: Matias Griese Date: Tue Aug 29 11:17:54 2017 +0300 Composer update commit 1bae601ad9ff9aedbcff0600bd26c76000bf38ff Author: Matias Griese Date: Tue Aug 29 11:17:38 2017 +0300 Add new FileCache class commit a4795a9df16752b3e758e65458be265ee7f15156 Author: Matias Griese Date: Tue Aug 29 10:37:14 2017 +0300 Add new SessionCache class commit 0644eac7dc56eb134922f7bc7db88cbd308ceeb4 Merge: 96ee41a3 7a3f1362 Author: Matias Griese Date: Tue Aug 29 08:56:04 2017 +0300 Merge branch 'develop' of https://github.com/getgrav/grav into 2.0 # Conflicts: # CHANGELOG.md # system/src/Grav/Common/Twig/TwigExtension.php commit 96ee41a3ddea70ea89c73b0034ec67858d4e97fd Author: Matias Griese Date: Fri Aug 18 12:28:13 2017 +0300 Follow PSR-2 in Grav\Framework commit d15eb0e6ad3d72295d36bce57d95673aef6b5362 Author: Matias Griese Date: Thu Aug 17 10:01:42 2017 +0300 Composer update commit c3066cd491dd6b53aa11c1a2ff4594353424085f Merge: 557a3b80 cafd3714 Author: Matias Griese Date: Thu Aug 17 09:59:29 2017 +0300 Merge branch 'develop' of https://github.com/getgrav/grav into 2.0 commit 557a3b80dd99690e3fde9baf030db4b72162025a Merge: f89fe0ea 51c3b6d1 Author: Matias Griese Date: Wed Aug 16 09:30:09 2017 +0300 Merge branch 'develop' of https://github.com/getgrav/grav into 2.0 # Conflicts: # composer.lock commit f89fe0ea1b24818384edf0a9b54638a4ee162842 Merge: ad5cddf2 04d7945f Author: Djamil Legato Date: Mon Aug 14 15:17:58 2017 -0700 Merge branch 'develop' into 2.0 commit ad5cddf222b13e41634ac2925783f9147230d375 Merge: fb0e086f 4b948e2c Author: Matias Griese Date: Mon Aug 14 20:15:43 2017 +0300 Merge branch 'develop' of https://github.com/getgrav/grav into 2.0 # Conflicts: # CHANGELOG.md commit fb0e086f5720dd441c1705f7392ef077b6786dde Author: Matias Griese Date: Thu Aug 10 21:13:14 2017 +0300 Composer: Force doctrine/cache to use v1.6 (with PHP 5.5 support) commit 53611c862700cbbeaaf8e2c85f0366329f44d5bb Author: Matias Griese Date: Thu Aug 10 21:08:42 2017 +0300 Composer: Use develop version of rockettheme/toolbox commit dbebd143919563c89e3983a8fbf121b0724f1ce0 Author: Matias Griese Date: Thu Aug 10 11:40:42 2017 +0300 Because of B/C issues, login/logout code needed to be moved into the Login plugin commit 6e511d0b2040e2f6155a0d3911a5c7e6dfdf8600 Author: Matias Griese Date: Tue Aug 8 17:50:26 2017 +0300 Improve login/logout logic commit 9685ab4a1899fbeb7fa60d965e2483c6ecd49a47 Author: Matias Griese Date: Tue Aug 8 14:31:53 2017 +0300 Added `$grav->login()` and `$grav->logout()` functions with event hooks for plugins Added events `onUserLoginAuthenticate`, `onUserLoginAuthorize`, `onUserLoginFailure`, `onUserLogin`, `onUserLogout` commit 670f0ce1d9d4a91a48661abdf9d5b5d35eeea526 Merge: 6fadaa07 1bfbb717 Author: mahagr Date: Wed Jul 19 15:35:05 2017 -0600 Merge branch 'develop' of https://github.com/getgrav/grav into 2.0 commit 6fadaa078944cd34ef1258bc47e26d5c16dd0f04 Author: Andy Miller Date: Wed Jul 19 14:28:57 2017 -0600 Added nicenumber twig filter commit 039d2286fb5aa3c152d15689462f6b26a95983b6 Author: mahagr Date: Tue Jul 18 15:37:04 2017 -0600 Add Object::getKey() function commit 0cde375953465f36bff0d1add90ac2687df09a25 Merge: a59fc7b8 bbc4a23c Author: mahagr Date: Tue Jul 18 15:34:41 2017 -0600 Merge branch 'develop' of https://github.com/getgrav/grav into 2.0 # Conflicts: # CHANGELOG.md commit a59fc7b8b8eb58a970e6d8ba28dc2bb10f9c8531 Merge: ee079635 4b723832 Author: Matias Griese Date: Tue Jul 4 09:51:44 2017 +0300 Merge branch 'develop' of https://github.com/getgrav/grav into 2.0 # Conflicts: # CHANGELOG.md commit ee079635bfb8e1b8d676136b189fa3422fb74788 Merge: ce319de9 90182e2c Author: Matias Griese Date: Wed Jun 28 10:37:46 2017 +0300 Merge branch 'develop' of https://github.com/getgrav/grav into 2.0 # Conflicts: # CHANGELOG.md commit ce319de97c41dbc99463f736f704abc4cdb62a0d Merge: 1bd3d269 5e329d34 Author: Matias Griese Date: Mon Jun 12 09:30:11 2017 +0300 Merge branch 'develop' of https://github.com/getgrav/grav into 2.0 # Conflicts: # CHANGELOG.md commit 1bd3d2693de328100bf9c5d710d17731393802be Merge: 3468f59a 947c920f Author: Matias Griese Date: Thu Jun 8 11:29:08 2017 +0300 Merge branch 'develop' of https://github.com/getgrav/grav into 2.0 commit 3468f59ac77beb984d11ee05d12630632830f6e5 Merge: c4fac41f ff8a8dde Author: Matias Griese Date: Tue Jun 6 12:20:04 2017 +0300 Merge branch 'develop' of https://github.com/getgrav/grav into 2.0 commit c4fac41fe32a2524ddc33381731ab0ea2a9d8244 Author: Matias Griese Date: Wed May 31 13:35:14 2017 +0300 Improve CacheTrait::getMultiple() commit 512aae350ecbd8ea697527f42893d10f7181c7e0 Author: Matias Griese Date: Wed May 31 13:34:04 2017 +0300 Fix cloning issues with ObjectCollection commit 673131926adac57b92e7ba72807755cb91c605fa Author: Matias Griese Date: Mon May 29 14:30:52 2017 +0300 Improve caching functions commit 668c9801eb2941f3c5011f77c18aa13fbbefe885 Merge: 6dbf704a c1edbab1 Author: Matias Griese Date: Mon May 29 10:12:20 2017 +0300 Merge branch 'develop' of https://github.com/getgrav/grav into 2.0 commit 6dbf704a1356562744808639d325707b46b485d9 Author: Matias Griese Date: Mon May 29 10:12:02 2017 +0300 Implement memory and chain cache commit 83943ce70d62a67ef9668631c9cd3835ccfdee88 Author: Matias Griese Date: Fri May 26 13:12:18 2017 +0300 Namespace and TTL not needed in MemoryCache adapter commit 99eff9587caf383c2198d97140ad4c5d9e013f4c Author: Matias Griese Date: Fri May 26 12:27:48 2017 +0300 Implement MemoryCache adapter commit 5ec844d883b0723c07afa5d8a0a821496c50b34b Merge: 7f3f634e 0ca58122 Author: Matias Griese Date: Fri May 26 11:57:00 2017 +0300 Merge branch 'develop' of https://github.com/getgrav/grav into 2.0 commit 7f3f634e3e047b67412b03135b5c429f8e0881fe Merge: 4cc502c4 b2070c8a Author: Matias Griese Date: Wed May 24 15:18:23 2017 +0300 Merge branch 'feature/debug-json' of https://github.com/getgrav/grav into 2.0 # Conflicts: # CHANGELOG.md commit b2070c8ab5f1da35f98abc696b0df9a7d6543878 Author: Matias Griese Date: Wed May 24 15:15:14 2017 +0300 Fix error on missing renderer if using unknown file extension commit 6ffc0625315bef61a62590604590e6323c08651c Merge: 7ca0f871 f22d6beb Author: Matias Griese Date: Wed May 24 15:13:11 2017 +0300 Merge branch 'develop' of https://github.com/getgrav/grav into feature/debug-json # Conflicts: # CHANGELOG.md commit 4cc502c477953144094da97224edddc5fe9aacb7 Merge: 78cb7671 7ca0f871 Author: Matias Griese Date: Mon May 22 15:10:41 2017 +0300 Merge branch 'feature/debug-json' of https://github.com/getgrav/grav into 2.0 commit 7ca0f8711cfbd9c5aae8ddd184b90cfe73a7323c Author: Matias Griese Date: Mon May 22 14:55:26 2017 +0300 Make it possible to include debug bar also into non-HTML responses commit 78cb767172f8a747fa724acb70bd6f5862f66139 Merge: 9d918ad1 d40cb3c7 Author: Matias Griese Date: Mon May 22 09:09:32 2017 +0300 Merge branch 'develop' of https://github.com/getgrav/grav into 2.0 # Conflicts: # CHANGELOG.md commit 9d918ad1114917dc0fc8b93d7be52dc21146e53d Merge: cb29d815 f7266ef7 Author: Matias Griese Date: Fri May 19 08:09:43 2017 +0300 Merge branch 'develop' of https://github.com/getgrav/grav into 2.0 commit cb29d81529e5282490bb21f44e3a48223c187a12 Merge: dd238462 890b0280 Author: Matias Griese Date: Thu May 18 17:45:34 2017 +0300 Merge branch 'develop' of https://github.com/getgrav/grav into 2.0 commit dd2384622cd06989d2435c8f25d3b54ef1a3958c Merge: 4b14a0db 5a93fcf9 Author: Matias Griese Date: Thu May 18 13:09:52 2017 +0300 Merge branch 'develop' of https://github.com/getgrav/grav into 2.0 commit 4b14a0db7ab745e5a3edcfaeda190ed9f02ad9c7 Merge: bf687e6f f3cecd8b Author: Matias Griese Date: Thu May 18 09:52:06 2017 +0300 Merge branch 'develop' of https://github.com/getgrav/grav into 2.0 commit bf687e6fa6cb8add68ec387acb57d9f97ac7f8d7 Merge: 01284161 ffe286ea Author: Matias Griese Date: Thu May 18 09:45:03 2017 +0300 Merge branch 'develop' of https://github.com/getgrav/grav into 2.0 # Conflicts: # CHANGELOG.md commit 012841617a62c5b7260940dfb8c381c55d093bdf Merge: 0269ec57 64965088 Author: Matias Griese Date: Wed May 17 09:27:02 2017 +0300 Merge branch 'develop' of https://github.com/getgrav/grav into 2.0 # Conflicts: # CHANGELOG.md commit 0269ec579ba891b9cafa9c584748aae9188037e5 Author: Matias Griese Date: Tue May 16 19:04:24 2017 +0300 Object collections: Allow property get, set and grouping for nested arrays commit 91a6224156d020ac5c592aac82fa06963d3670c6 Author: Matias Griese Date: Tue May 16 19:03:23 2017 +0300 Rename Object::offsetLoad_*() functions, nested array access commit b4801560be0b9c7cf18c51d1bed82b09149f5aef Author: Matias Griese Date: Tue May 16 12:24:41 2017 +0300 Update docblocks commit c7bcbeabd9b284503ef61b812b815893fa90af31 Author: Matias Griese Date: Tue May 16 12:00:49 2017 +0300 Add support for loading properties in Object class commit 41c130cd43e059fe25659edd9ec6939b4fa892ed Merge: 4f27ff11 d11099c0 Author: Matias Griese Date: Tue May 16 10:54:16 2017 +0300 Merge branch 'develop' of https://github.com/getgrav/grav into 2.0 # Conflicts: # composer.lock commit 4f27ff1100d68eed496e1c619f1c9f0355bd544b Merge: d1654a3e 8ff20034 Author: Djamil Legato Date: Fri May 12 14:08:47 2017 -0700 Merge branch 'develop' into 2.0 # Conflicts: # composer.json # composer.lock commit d1654a3e32022d8f09c5a84b5183fb833e81ef84 Author: Matias Griese Date: Thu May 11 13:34:43 2017 +0300 Added `Grav\Framework\Cache` classes providing PSR-16 `Simple Cache` implementation commit 77c2e47b9566263b6925aa046eb4c61ddc95c155 Author: Matias Griese Date: Thu May 11 13:20:01 2017 +0300 Greatly simplify Object classes commit ec4d451d7f904581e7271531a8fe73d4e170e759 Author: Matias Griese Date: Wed May 10 11:34:23 2017 +0300 Fix for PHP 5.5 support in ObjectCollectionTrait commit 65c0c4438f5da2e3a6b368888453b629ba52eca5 Author: Matias Griese Date: Wed May 10 11:02:51 2017 +0300 Use older version of doctrine/collections (PHP 5.5 support) commit 6685dadddf4a5d32b1d96659f9bd6c9d31d65fb1 Author: Matias Griese Date: Wed May 10 10:21:01 2017 +0300 Oops, accidentally merged local 2.0 branch into develop, revert GravTrait deprecation and add back new Framework classes --- .dependencies | 12 +- CHANGELOG.md | 112 +++- backup/.gitkeep | 0 composer.json | 5 +- composer.lock | 433 ++++++++++----- index.php | 2 +- system/assets/whoops.css | 2 +- system/blueprints/pages/default.yaml | 25 + system/blueprints/pages/modular.yaml | 41 +- system/blueprints/user/account.yaml | 1 + system/config/system.yaml | 1 + system/defines.php | 6 +- system/router.php | 2 +- system/src/Grav/Common/Assets.php | 2 +- system/src/Grav/Common/Backup/ZipBackup.php | 11 +- system/src/Grav/Common/Browser.php | 2 +- system/src/Grav/Common/Cache.php | 10 +- system/src/Grav/Common/Composer.php | 2 +- .../src/Grav/Common/Config/CompiledBase.php | 2 +- .../Grav/Common/Config/CompiledBlueprints.php | 5 +- .../src/Grav/Common/Config/CompiledConfig.php | 2 +- .../Grav/Common/Config/CompiledLanguages.php | 4 +- system/src/Grav/Common/Config/Config.php | 3 +- .../Grav/Common/Config/ConfigFileFinder.php | 2 +- system/src/Grav/Common/Config/Languages.php | 2 +- system/src/Grav/Common/Config/Setup.php | 17 +- system/src/Grav/Common/Data/Blueprint.php | 2 +- .../src/Grav/Common/Data/BlueprintSchema.php | 10 +- system/src/Grav/Common/Data/Blueprints.php | 6 +- system/src/Grav/Common/Data/Data.php | 3 +- system/src/Grav/Common/Data/DataInterface.php | 2 +- system/src/Grav/Common/Data/Validation.php | 42 +- .../Grav/Common/Data/ValidationException.php | 2 +- system/src/Grav/Common/Debugger.php | 51 +- system/src/Grav/Common/Errors/BareHandler.php | 2 +- system/src/Grav/Common/Errors/Errors.php | 2 +- .../Grav/Common/Errors/SimplePageHandler.php | 8 +- .../src/Grav/Common/Errors/SystemFacade.php | 2 +- system/src/Grav/Common/File/CompiledFile.php | 8 +- .../src/Grav/Common/File/CompiledJsonFile.php | 2 +- .../Grav/Common/File/CompiledMarkdownFile.php | 2 +- .../src/Grav/Common/File/CompiledYamlFile.php | 2 +- system/src/Grav/Common/Filesystem/Folder.php | 21 +- .../RecursiveFolderFilterIterator.php | 4 +- .../Grav/Common/GPM/AbstractCollection.php | 2 +- .../GPM/Common/AbstractPackageCollection.php | 2 +- .../Common/GPM/Common/CachedCollection.php | 2 +- system/src/Grav/Common/GPM/Common/Package.php | 2 +- system/src/Grav/Common/GPM/GPM.php | 24 +- system/src/Grav/Common/GPM/Installer.php | 2 +- system/src/Grav/Common/GPM/Licenses.php | 2 +- .../GPM/Local/AbstractPackageCollection.php | 2 +- system/src/Grav/Common/GPM/Local/Package.php | 2 +- system/src/Grav/Common/GPM/Local/Packages.php | 2 +- system/src/Grav/Common/GPM/Local/Plugins.php | 2 +- system/src/Grav/Common/GPM/Local/Themes.php | 2 +- .../GPM/Remote/AbstractPackageCollection.php | 2 +- .../src/Grav/Common/GPM/Remote/GravCore.php | 35 +- system/src/Grav/Common/GPM/Remote/Package.php | 2 +- .../src/Grav/Common/GPM/Remote/Packages.php | 2 +- system/src/Grav/Common/GPM/Remote/Plugins.php | 2 +- system/src/Grav/Common/GPM/Remote/Themes.php | 2 +- system/src/Grav/Common/GPM/Response.php | 2 +- system/src/Grav/Common/GPM/Upgrader.php | 23 +- system/src/Grav/Common/Getters.php | 2 +- system/src/Grav/Common/Grav.php | 22 +- system/src/Grav/Common/GravTrait.php | 9 +- system/src/Grav/Common/Helpers/Base32.php | 8 +- system/src/Grav/Common/Helpers/Excerpts.php | 23 +- system/src/Grav/Common/Helpers/Exif.php | 9 +- system/src/Grav/Common/Helpers/Truncator.php | 10 +- system/src/Grav/Common/Inflector.php | 2 +- system/src/Grav/Common/Iterator.php | 2 +- system/src/Grav/Common/Language/Language.php | 20 +- .../Grav/Common/Language/LanguageCodes.php | 20 +- system/src/Grav/Common/Markdown/Parsedown.php | 2 +- .../Grav/Common/Markdown/ParsedownExtra.php | 4 +- .../Common/Markdown/ParsedownGravTrait.php | 53 +- system/src/Grav/Common/Page/Collection.php | 21 +- system/src/Grav/Common/Page/Header.php | 2 +- system/src/Grav/Common/Page/Media.php | 2 +- .../Grav/Common/Page/Medium/AbstractMedia.php | 2 +- .../Grav/Common/Page/Medium/AudioMedium.php | 2 +- .../Grav/Common/Page/Medium/GlobalMedia.php | 2 +- .../src/Grav/Common/Page/Medium/ImageFile.php | 2 +- .../Grav/Common/Page/Medium/ImageMedium.php | 2 +- system/src/Grav/Common/Page/Medium/Link.php | 2 +- system/src/Grav/Common/Page/Medium/Medium.php | 22 +- .../Grav/Common/Page/Medium/MediumFactory.php | 6 +- .../Common/Page/Medium/ParsedownHtmlTrait.php | 2 +- .../Page/Medium/RenderableInterface.php | 2 +- .../Common/Page/Medium/StaticImageMedium.php | 2 +- .../Common/Page/Medium/StaticResizeTrait.php | 2 +- .../Page/Medium/ThumbnailImageMedium.php | 6 +- .../Grav/Common/Page/Medium/VideoMedium.php | 28 +- system/src/Grav/Common/Page/Page.php | 84 ++- system/src/Grav/Common/Page/Pages.php | 10 +- system/src/Grav/Common/Page/Types.php | 2 +- system/src/Grav/Common/Plugin.php | 17 +- system/src/Grav/Common/Plugins.php | 2 +- .../Common/Processors/AssetsProcessor.php | 2 +- .../Processors/ConfigurationProcessor.php | 2 +- .../Processors/DebuggerAssetsProcessor.php | 2 +- .../Processors/DebuggerInitProcessor.php | 2 +- .../Common/Processors/ErrorsProcessor.php | 2 +- .../Common/Processors/InitializeProcessor.php | 10 +- .../Grav/Common/Processors/PagesProcessor.php | 2 +- .../Common/Processors/PluginsProcessor.php | 2 +- .../Grav/Common/Processors/ProcessorBase.php | 2 +- .../Common/Processors/ProcessorInterface.php | 2 +- .../Common/Processors/RenderProcessor.php | 2 +- .../Common/Processors/SiteSetupProcessor.php | 2 +- .../Grav/Common/Processors/TasksProcessor.php | 2 +- .../Common/Processors/ThemesProcessor.php | 2 +- .../Grav/Common/Processors/TwigProcessor.php | 2 +- .../Common/Service/AssetsServiceProvider.php | 2 +- .../Common/Service/ConfigServiceProvider.php | 2 +- .../Common/Service/ErrorServiceProvider.php | 2 +- .../Common/Service/LoggerServiceProvider.php | 2 +- .../Service/MessagesServiceProvider.php | 30 - .../Common/Service/OutputServiceProvider.php | 2 +- .../Common/Service/PageServiceProvider.php | 2 +- .../Common/Service/SessionServiceProvider.php | 105 ++++ .../Common/Service/StreamsServiceProvider.php | 2 +- .../Common/Service/TaskServiceProvider.php | 2 +- system/src/Grav/Common/Session.php | 141 ++--- system/src/Grav/Common/Taxonomy.php | 2 +- system/src/Grav/Common/Theme.php | 2 +- system/src/Grav/Common/Themes.php | 2 +- .../Common/Twig/Node/TwigNodeMarkdown.php | 35 ++ .../Grav/Common/Twig/Node/TwigNodeScript.php | 102 ++++ .../Grav/Common/Twig/Node/TwigNodeStyle.php | 98 ++++ .../Grav/Common/Twig/Node/TwigNodeSwitch.php | 71 +++ .../Common/Twig/Node/TwigNodeTryCatch.php | 52 ++ .../TokenParser/TwigTokenParserMarkdown.php | 53 ++ .../TokenParser/TwigTokenParserScript.php | 100 ++++ .../Twig/TokenParser/TwigTokenParserStyle.php | 99 ++++ .../TokenParser/TwigTokenParserSwitch.php | 138 +++++ .../TokenParser/TwigTokenParserTryCatch.php | 68 +++ system/src/Grav/Common/Twig/Twig.php | 2 +- .../src/Grav/Common/Twig/TwigEnvironment.php | 2 +- system/src/Grav/Common/Twig/TwigExtension.php | 306 +++++++--- .../Grav/Common/Twig/WriteCacheFileTrait.php | 2 +- system/src/Grav/Common/Uri.php | 524 +++++++++--------- .../src/Grav/Common/User/Authentication.php | 2 +- system/src/Grav/Common/User/Group.php | 62 ++- system/src/Grav/Common/User/User.php | 35 +- system/src/Grav/Common/Utils.php | 235 +++++--- system/src/Grav/Console/Cli/BackupCommand.php | 2 +- system/src/Grav/Console/Cli/CleanCommand.php | 2 +- .../Grav/Console/Cli/ClearCacheCommand.php | 2 +- .../src/Grav/Console/Cli/ComposerCommand.php | 2 +- .../src/Grav/Console/Cli/InstallCommand.php | 2 +- .../Grav/Console/Cli/NewProjectCommand.php | 2 +- .../src/Grav/Console/Cli/SandboxCommand.php | 2 +- system/src/Grav/Console/ConsoleCommand.php | 2 +- system/src/Grav/Console/ConsoleTrait.php | 2 +- .../Grav/Console/Gpm/DirectInstallCommand.php | 2 +- system/src/Grav/Console/Gpm/IndexCommand.php | 2 +- system/src/Grav/Console/Gpm/InfoCommand.php | 2 +- .../src/Grav/Console/Gpm/InstallCommand.php | 2 +- .../Grav/Console/Gpm/SelfupgradeCommand.php | 6 +- .../src/Grav/Console/Gpm/UninstallCommand.php | 4 +- system/src/Grav/Console/Gpm/UpdateCommand.php | 2 +- .../src/Grav/Console/Gpm/VersionCommand.php | 2 +- .../Grav/Console/TerminalObjects/Table.php | 2 +- .../Grav/Framework/Cache/AbstractCache.php | 30 + .../Framework/Cache/Adapter/ChainCache.php | 197 +++++++ .../Framework/Cache/Adapter/DoctrineCache.php | 123 ++++ .../Framework/Cache/Adapter/FileCache.php | 210 +++++++ .../Framework/Cache/Adapter/MemoryCache.php | 61 ++ .../Framework/Cache/Adapter/SessionCache.php | 78 +++ .../Grav/Framework/Cache/CacheInterface.php | 27 + .../src/Grav/Framework/Cache/CacheTrait.php | 341 ++++++++++++ .../Cache/Exception/CacheException.php | 19 + .../Exception/InvalidArgumentException.php | 19 + .../Collection/AbstractFileCollection.php | 227 ++++++++ .../Collection/AbstractLazyCollection.php | 11 +- .../Framework/Collection/ArrayCollection.php | 29 +- .../Collection/CollectionInterface.php | 10 +- .../Framework/Collection/FileCollection.php | 211 +------ .../Collection/FileCollectionInterface.php | 28 + .../Framework/ContentBlock/ContentBlock.php | 230 ++++++++ .../ContentBlock/ContentBlockInterface.php | 75 +++ .../Grav/Framework/ContentBlock/HtmlBlock.php | 385 +++++++++++++ .../ContentBlock/HtmlBlockInterface.php | 94 ++++ .../Object/Access/ArrayAccessTrait.php | 64 +++ .../Object/Access/NestedArrayAccessTrait.php | 64 +++ .../Access/NestedPropertyCollectionTrait.php | 124 +++++ .../Object/Access/NestedPropertyTrait.php | 182 ++++++ .../Object/Access/OverloadedPropertyTrait.php | 64 +++ .../src/Grav/Framework/Object/ArrayObject.php | 26 + .../Object/Base/ObjectCollectionTrait.php | 190 +++++++ .../Framework/Object/Base/ObjectTrait.php | 174 ++++++ .../Interfaces/NestedObjectInterface.php | 57 ++ .../Interfaces/ObjectCollectionInterface.php | 66 +++ .../Object/Interfaces/ObjectInterface.php | 59 ++ .../src/Grav/Framework/Object/LazyObject.php | 26 + .../Framework/Object/ObjectCollection.php | 48 ++ .../Object/Property/ArrayPropertyTrait.php | 109 ++++ .../Object/Property/LazyPropertyTrait.php | 117 ++++ .../Object/Property/MixedPropertyTrait.php | 122 ++++ .../Object/Property/ObjectPropertyTrait.php | 203 +++++++ .../Grav/Framework/Object/PropertyObject.php | 26 + .../src/Grav/Framework/Psr7/AbstractUri.php | 406 ++++++++++++++ system/src/Grav/Framework/Route/Route.php | 279 ++++++++++ .../src/Grav/Framework/Route/RouteFactory.php | 131 +++++ system/src/Grav/Framework/Uri/Uri.php | 213 +++++++ system/src/Grav/Framework/Uri/UriFactory.php | 159 ++++++ .../src/Grav/Framework/Uri/UriPartsFilter.php | 140 +++++ user/config/system.yaml | 4 +- user/pages/01.home/default.md | 15 +- user/pages/02.typography/default.md | 155 ++++++ 213 files changed, 8060 insertions(+), 1333 deletions(-) delete mode 100644 backup/.gitkeep delete mode 100644 system/src/Grav/Common/Service/MessagesServiceProvider.php create mode 100644 system/src/Grav/Common/Service/SessionServiceProvider.php create mode 100644 system/src/Grav/Common/Twig/Node/TwigNodeMarkdown.php create mode 100644 system/src/Grav/Common/Twig/Node/TwigNodeScript.php create mode 100644 system/src/Grav/Common/Twig/Node/TwigNodeStyle.php create mode 100644 system/src/Grav/Common/Twig/Node/TwigNodeSwitch.php create mode 100644 system/src/Grav/Common/Twig/Node/TwigNodeTryCatch.php create mode 100644 system/src/Grav/Common/Twig/TokenParser/TwigTokenParserMarkdown.php create mode 100644 system/src/Grav/Common/Twig/TokenParser/TwigTokenParserScript.php create mode 100644 system/src/Grav/Common/Twig/TokenParser/TwigTokenParserStyle.php create mode 100644 system/src/Grav/Common/Twig/TokenParser/TwigTokenParserSwitch.php create mode 100644 system/src/Grav/Common/Twig/TokenParser/TwigTokenParserTryCatch.php create mode 100644 system/src/Grav/Framework/Cache/AbstractCache.php create mode 100644 system/src/Grav/Framework/Cache/Adapter/ChainCache.php create mode 100644 system/src/Grav/Framework/Cache/Adapter/DoctrineCache.php create mode 100644 system/src/Grav/Framework/Cache/Adapter/FileCache.php create mode 100644 system/src/Grav/Framework/Cache/Adapter/MemoryCache.php create mode 100644 system/src/Grav/Framework/Cache/Adapter/SessionCache.php create mode 100644 system/src/Grav/Framework/Cache/CacheInterface.php create mode 100644 system/src/Grav/Framework/Cache/CacheTrait.php create mode 100644 system/src/Grav/Framework/Cache/Exception/CacheException.php create mode 100644 system/src/Grav/Framework/Cache/Exception/InvalidArgumentException.php create mode 100644 system/src/Grav/Framework/Collection/AbstractFileCollection.php create mode 100644 system/src/Grav/Framework/Collection/FileCollectionInterface.php create mode 100644 system/src/Grav/Framework/ContentBlock/ContentBlock.php create mode 100644 system/src/Grav/Framework/ContentBlock/ContentBlockInterface.php create mode 100644 system/src/Grav/Framework/ContentBlock/HtmlBlock.php create mode 100644 system/src/Grav/Framework/ContentBlock/HtmlBlockInterface.php create mode 100644 system/src/Grav/Framework/Object/Access/ArrayAccessTrait.php create mode 100644 system/src/Grav/Framework/Object/Access/NestedArrayAccessTrait.php create mode 100644 system/src/Grav/Framework/Object/Access/NestedPropertyCollectionTrait.php create mode 100644 system/src/Grav/Framework/Object/Access/NestedPropertyTrait.php create mode 100644 system/src/Grav/Framework/Object/Access/OverloadedPropertyTrait.php create mode 100644 system/src/Grav/Framework/Object/ArrayObject.php create mode 100644 system/src/Grav/Framework/Object/Base/ObjectCollectionTrait.php create mode 100644 system/src/Grav/Framework/Object/Base/ObjectTrait.php create mode 100644 system/src/Grav/Framework/Object/Interfaces/NestedObjectInterface.php create mode 100644 system/src/Grav/Framework/Object/Interfaces/ObjectCollectionInterface.php create mode 100644 system/src/Grav/Framework/Object/Interfaces/ObjectInterface.php create mode 100644 system/src/Grav/Framework/Object/LazyObject.php create mode 100644 system/src/Grav/Framework/Object/ObjectCollection.php create mode 100644 system/src/Grav/Framework/Object/Property/ArrayPropertyTrait.php create mode 100644 system/src/Grav/Framework/Object/Property/LazyPropertyTrait.php create mode 100644 system/src/Grav/Framework/Object/Property/MixedPropertyTrait.php create mode 100644 system/src/Grav/Framework/Object/Property/ObjectPropertyTrait.php create mode 100644 system/src/Grav/Framework/Object/PropertyObject.php create mode 100644 system/src/Grav/Framework/Psr7/AbstractUri.php create mode 100644 system/src/Grav/Framework/Route/Route.php create mode 100644 system/src/Grav/Framework/Route/RouteFactory.php create mode 100644 system/src/Grav/Framework/Uri/Uri.php create mode 100644 system/src/Grav/Framework/Uri/UriFactory.php create mode 100644 system/src/Grav/Framework/Uri/UriPartsFilter.php create mode 100644 user/pages/02.typography/default.md diff --git a/.dependencies b/.dependencies index 3b6e375bc..86f4a79a5 100644 --- a/.dependencies +++ b/.dependencies @@ -11,9 +11,9 @@ git: url: https://github.com/getgrav/grav-plugin-markdown-notices path: user/plugins/markdown-notices branch: master - antimatter: - url: https://github.com/getgrav/grav-theme-antimatter - path: user/themes/antimatter + quark: + url: https://github.com/getgrav/grav-theme-quark + path: user/themes/quark branch: master links: problems: @@ -28,7 +28,7 @@ links: src: grav-plugin-markdown-notices path: user/plugins/markdown-notices scm: github - antimatter: - src: grav-theme-antimatter - path: user/themes/antimatter + quark: + src: grav-theme-quark + path: user/themes/quark scm: github diff --git a/CHANGELOG.md b/CHANGELOG.md index d79785aa8..676fd9e7b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,95 @@ +# v1.4.0-rc.3 +## mm/dd/2018 + +1. [](#new) + * Added `Grav\Framework\Uri` classes extending PSR-7 `HTTP message UriInterface` implementation + * Added `Grav\Framework\Route` classes to allow route/link manipulation + * Added `$grav['uri]->getCurrentUri()` method to get `Grav\Framework\Uri\Uri` instance for the current URL + * Added `$grav['uri]->getCurrentRoute()` method to get `Grav\Framework\Route\Route` instance for the current URL + * Added ability to have `php` version dependencies in GPM assets + * Added new `{% switch %}` twig tag for more elegant if statements + * Added new `{% markdown %}` twig tag + * Added **Route Overrides** to the default page blueprint +1. [](#improved) + * Vendor library updated to latest + * Improved `Session` initialization + * Added ability to set a `theme_var()` option in page frontmatter + * Force clearing PHP `clearstatcache` and `opcache-reset` on `Cache::clear()` +1. [](#bugfix) + * Fixed issue with image alt tag always getting empted out unless set in markdown + * Fixed issue with remote PHP version determination for Grav updates [#1883](https://github.com/getgrav/grav/issues/1883) + +# v1.4.0-rc.2 +## 02/15/2018 + +1. [](#new) + * Added new `Collection::toExtendedArray()` method that's particularly useful for Json output of data + * Added new `|yaml_encode` and `|yaml_decode` Twig filter to convert to and from YAML + * Added new `read_file()` Twig function to allow you to load and display a file in Twig (Supports streams and regular paths) + * Added a new `Medium::exists()` method to check for file existence +1. [](#improved) + * Better `Page.collection()` filtering support including ability to have non-published pages in collections + * Stopped Chrome from auto-completing admin user profile form [#1847](https://github.com/getgrav/grav/issues/1847) + * Support for empty `switch` field like a `checkbox` +1. [](#bugfix) + * Properly validate YAML blueprint fields so admin can save as proper YAML now [addresses many issues] + * Fixed OpenGraph metatags so only Twitter uses `name=`, and all others use `property=` [#1849](https://github.com/getgrav/grav/issues/1849) + * Fixed an issue with `evaluate()` and `evaluate_twig()` Twig functions that throws invalid template error + * Fixed issue with `|sort_by_key` twig filter if the input was null or not an array + +# v1.4.0-rc.1 +## 01/22/2018 + +1. [](#new) + * Moved Twig `urlFunc()` to `Utils::url()` as its so darn handy +1. [](#improved) + * Made `modular` blueprint more flexible + * Code optimizations to `Utils` class [#1830](https://github.com/getgrav/grav/pull/1830) + * Objects: Add protected function `getElement()` to get serialized value for a single property + * `ObjectPropertyTrait`: Added protected functions `isPropertyLoaded()`, `offsetLoad()`, `offsetPrepare()` and `offsetSerialize()` + * `Grav\Framework\Cache`: Allow unlimited TTL + * Optimizations & refactoring to the test suite [#1779](https://github.com/getgrav/grav/pull/1779) + * Slight modification of Whoops error colors + * Updated vendor libs to latest +1. [](#bugfix) + * Date ordering should always be numeric [#1810](https://github.com/getgrav/grav/issues/1810) + +# v1.4.0-beta.3 +## 12/29/2017 + +1. [](#bugfix) + * Fix for base paths containing special characters [#1799](https://github.com/getgrav/grav/issues/1799) + * Fix for session cookies in paths containing special characters + * Fix for `vundefined` error for version numbers in GPM [form#222](https://github.com/getgrav/grav-plugin-form/issues/222) +1. [](#improved) + * Added new configuration option `system.session.initialize` to delay session initialization if needed by a plugin + +# v1.4.0-beta.2 +## 12/18/2017 + +1. [](#new) + * Transferred overall copyright from RocketTheme, LLC, to Trilby Media LLC + * Added `theme_var`, `header_var` and `body_class` Twig functions for themes +1. [](#improved) + * Updated vendor libraries to latest versions + * Removed constructor from `ObjectInterface` +1. [](#bugfix) + * Fixed `BadMethodCallException` thrown in GPM updates [#1784](https://github.com/getgrav/grav/issues/1784) + +# v1.4.0-beta.1 +## 12/11/2017 + +1. [](#new) + * Added `Grav\Framework\Cache` classes providing PSR-16 `Simple Cache` implementation + * Added `Grav\Framework\ContentBlock` classes for nested HTML blocks with CSS/JS assets + * Added `Grav\Framework\Object` classes for creating collections of objects + * Added `|nicenumber` Twig filter + * Added `{% try %} ... {% catch %} Error: {{ e.message }} {% endcatch %}` tag to allow basic exception handling inside Twig + * Added `{% script %}` and `{% style %}` tags for Twig templates + * Deprecated GravTrait +1. [](#improved) + * Make it possible to include debug bar also into non-HTML responses + # v1.3.11 ## mm/dd/2017 @@ -10,14 +102,14 @@ ## 12/06/2017 1. [](#bugfix) - * Reverted GPM Local pull request as it broken admin [#1742](https://github.com/getgrav/grav/issues/1742) + * Reverted GPM Local pull request as it broken admin [#1742](https://github.com/getgrav/grav/issues/1742) # v1.3.9 ## 12/05/2017 1. [](#new) * Added new core Twig templates for `partials/metadata.html.twig` and `partials/messages.html.twig` - * Added ability to work with GPM locally [#1742](https://github.com/getgrav/grav/issues/1742) + * Added ability to work with GPM locally [#1742](https://github.com/getgrav/grav/issues/1742) * Added new HTML5 audio controls [#1756](https://github.com/getgrav/grav/issues/1756) * Added `Medium::copy()` method to create a copy of a medium object * Added new `force_lowercase_urls` functionality on routes and slugs @@ -41,15 +133,15 @@ * Fixed token creation issue with `Uri` params like `/id:3` * Fixed CSS Pipeline failing with Google remote fonts if the file was minified [#1261](https://github.com/getgrav/grav-plugin-admin/issues/1261) * Forced `field.multiple: true` to allow use of min/max options in `checkboxes.validate` - + # v1.3.8 ## 10/26/2017 1. [](#new) * Added Page `media_order` capability to manually order page media via a page header 1. [](#bugfix) - * Fixed GPM update issue with filtered slugs [#1711](https://github.com/getgrav/grav/issues/1711) - * Fixed issue with missing image file not throwing 404 properly [#1713](https://github.com/getgrav/grav/issues/1713) + * Fixed GPM update issue with filtered slugs [#1711](https://github.com/getgrav/grav/issues/1711) + * Fixed issue with missing image file not throwing 404 properly [#1713](https://github.com/getgrav/grav/issues/1713) # v1.3.7 ## 10/18/2017 @@ -65,20 +157,20 @@ 1. [](#bugfix) * Regression: Ajax error in Nginx [admin#1244](https://github.com/getgrav/grav-plugin-admin/issues/1244) * Remove the `_url=$uri` portion of the the Nginx `try_files` command [admin#1244](https://github.com/getgrav/grav-plugin-admin/issues/1244) - + # v1.3.5 ## 10/11/2017 1. [](#improved) * Refactored `URI` class with numerous bug fixes, and optimizations * Override `system.media.upload_limit` with PHP's `post_max_size` or `upload_max_filesize` - * Updated `bin/grav clean` command to remove unnecessary vendor files (save some bytes) + * Updated `bin/grav clean` command to remove unnecessary vendor files (save some bytes) * Added a `http_status_code` Twig function to allow setting HTTP status codes from Twig directly. * Deter XSS attacks via URI path/uri methods (credit:newbthenewbd) * Added support for `$uri->toArray()` and `(string)$uri` * Added support for `type` on `Asstes::addInlineJs()` [#1683](https://github.com/getgrav/grav/pull/1683) 1. [](#bugfix) - * Fixed method signature error with `GPM\InstallCommand::processPackage()` [#1682](https://github.com/getgrav/grav/pull/1682) + * Fixed method signature error with `GPM\InstallCommand::processPackage()` [#1682](https://github.com/getgrav/grav/pull/1682) # v1.3.4 ## 09/29/2017 @@ -92,7 +184,7 @@ * Improved support for Assets with query strings [#1451](https://github.com/getgrav/grav/issues/1451) * Twig extension cleanup 1. [](#bugfix) - * Fixed an issue where fallback was not supporting dynamic page generation + * Fixed an issue where fallback was not supporting dynamic page generation * Fixed issue with Image query string not being fully URL encoded [#1622](https://github.com/getgrav/grav/issues/1622) * Fixed `Page::summary()` when using delimiter and multibyte UTF8 Characters [#1644](https://github.com/getgrav/grav/issues/1644) * Fixed missing `.json` thumbnail throwing error when adding media [grav-plugin-admin#1156](https://github.com/getgrav/grav-plugin-admin/issues/1156) @@ -135,7 +227,7 @@ * Allow `session.timeout` field to be set to `0` via blueprints [#1598](https://github.com/getgrav/grav/issues/1598) * Fixed `Data::exists()` and `Data::raw()` functions breaking if `Data::file()` hasn't been called with non-null value * Fixed parent theme auto-loading in child themes of Gantry 5 - + # v1.3.1 ## 07/19/2017 diff --git a/backup/.gitkeep b/backup/.gitkeep deleted file mode 100644 index e69de29bb..000000000 diff --git a/composer.json b/composer.json index 5b79960db..788cd0175 100644 --- a/composer.json +++ b/composer.json @@ -16,15 +16,16 @@ "symfony/event-dispatcher": "~2.8", "symfony/var-dumper": "~2.8", "symfony/polyfill-iconv": "~1.0", - "doctrine/cache": "1.6.*", + "doctrine/cache": "^1.6", "doctrine/collections": "1.3", + "psr/simple-cache": "^1.0", "filp/whoops": "~2.0", "matthiasmullie/minify": "^1.3", "monolog/monolog": "~1.0", "gregwar/image": "2.*", "donatj/phpuseragentparser": "~0.3", "pimple/pimple": "~3.0", - "rockettheme/toolbox": "~1.0", + "rockettheme/toolbox": "~1.3", "maximebf/debugbar": "~1.10", "ext-mbstring": "*", "ext-openssl": "*", diff --git a/composer.lock b/composer.lock index 449dafac3..e13390d56 100644 --- a/composer.lock +++ b/composer.lock @@ -4,20 +4,20 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "content-hash": "6ee956d337b26a464deac44ba90da27d", + "content-hash": "863f839a5a0e0ebbf94742efc24cb6a4", "packages": [ { "name": "antoligy/dom-string-iterators", - "version": "v1.0.0", + "version": "v1.0.1", "source": { "type": "git", "url": "https://github.com/antoligy/dom-string-iterators.git", - "reference": "9a624b082493fee9b972840dbd677494edb94cf7" + "reference": "fae88f66e1970d68c5585fc42db44f1217bf74e6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/antoligy/dom-string-iterators/zipball/9a624b082493fee9b972840dbd677494edb94cf7", - "reference": "9a624b082493fee9b972840dbd677494edb94cf7", + "url": "https://api.github.com/repos/antoligy/dom-string-iterators/zipball/fae88f66e1970d68c5585fc42db44f1217bf74e6", + "reference": "fae88f66e1970d68c5585fc42db44f1217bf74e6", "shasum": "" }, "require": { @@ -31,7 +31,7 @@ }, "notification-url": "https://packagist.org/downloads/", "license": [ - "Public Domain" + "CC0-1.0" ], "authors": [ { @@ -48,7 +48,7 @@ } ], "description": "Composer package for DOMWordsIterator and DOMLettersIterator", - "time": "2015-11-04T17:33:14+00:00" + "time": "2018-02-03T16:01:11+00:00" }, { "name": "composer/ca-bundle", @@ -295,19 +295,20 @@ }, { "name": "erusev/parsedown", - "version": "1.6.4", + "version": "1.7.1", "source": { "type": "git", "url": "https://github.com/erusev/parsedown.git", - "reference": "fbe3fe878f4fe69048bb8a52783a09802004f548" + "reference": "92e9c27ba0e74b8b028b111d1b6f956a15c01fc1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/erusev/parsedown/zipball/fbe3fe878f4fe69048bb8a52783a09802004f548", - "reference": "fbe3fe878f4fe69048bb8a52783a09802004f548", + "url": "https://api.github.com/repos/erusev/parsedown/zipball/92e9c27ba0e74b8b028b111d1b6f956a15c01fc1", + "reference": "92e9c27ba0e74b8b028b111d1b6f956a15c01fc1", "shasum": "" }, "require": { + "ext-mbstring": "*", "php": ">=5.3.0" }, "require-dev": { @@ -336,7 +337,7 @@ "markdown", "parser" ], - "time": "2017-11-14T20:44:03+00:00" + "time": "2018-03-08T01:11:30+00:00" }, { "name": "erusev/parsedown-extra", @@ -488,17 +489,17 @@ }, { "name": "gregwar/image", - "version": "v2.0.21", + "version": "v2.0.22", "target-dir": "Gregwar/Image", "source": { "type": "git", "url": "https://github.com/Gregwar/Image.git", - "reference": "c9899e4c71009338f89a8c63c12c681692533ede" + "reference": "c506d57d7fb5e67961d4eea6540ae8e23dffd406" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Gregwar/Image/zipball/c9899e4c71009338f89a8c63c12c681692533ede", - "reference": "c9899e4c71009338f89a8c63c12c681692533ede", + "url": "https://api.github.com/repos/Gregwar/Image/zipball/c506d57d7fb5e67961d4eea6540ae8e23dffd406", + "reference": "c506d57d7fb5e67961d4eea6540ae8e23dffd406", "shasum": "" }, "require": { @@ -536,20 +537,20 @@ "gd", "image" ], - "time": "2017-01-24T09:29:39+00:00" + "time": "2018-01-31T17:40:17+00:00" }, { "name": "league/climate", - "version": "3.2.1", + "version": "3.2.4", "source": { "type": "git", "url": "https://github.com/thephpleague/climate.git", - "reference": "b103fc8faa3780c802cc507d5f0ff534ecc94fb5" + "reference": "ca70f67f7739cca823eba0ad98f8130bca226bf0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/climate/zipball/b103fc8faa3780c802cc507d5f0ff534ecc94fb5", - "reference": "b103fc8faa3780c802cc507d5f0ff534ecc94fb5", + "url": "https://api.github.com/repos/thephpleague/climate/zipball/ca70f67f7739cca823eba0ad98f8130bca226bf0", + "reference": "ca70f67f7739cca823eba0ad98f8130bca226bf0", "shasum": "" }, "require": { @@ -587,20 +588,20 @@ "php", "terminal" ], - "time": "2016-04-04T20:24:59+00:00" + "time": "2016-10-30T22:18:25+00:00" }, { "name": "matthiasmullie/minify", - "version": "1.3.57", + "version": "1.3.59", "source": { "type": "git", "url": "https://github.com/matthiasmullie/minify.git", - "reference": "7559ac0806f39f89f949c917e6612dda8154488b" + "reference": "62dac3bce06de66f0d71fe6490cf1c508d3c3ff7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/matthiasmullie/minify/zipball/7559ac0806f39f89f949c917e6612dda8154488b", - "reference": "7559ac0806f39f89f949c917e6612dda8154488b", + "url": "https://api.github.com/repos/matthiasmullie/minify/zipball/62dac3bce06de66f0d71fe6490cf1c508d3c3ff7", + "reference": "62dac3bce06de66f0d71fe6490cf1c508d3c3ff7", "shasum": "" }, "require": { @@ -647,20 +648,20 @@ "minifier", "minify" ], - "time": "2017-12-06T12:46:42+00:00" + "time": "2018-02-02T12:44:18+00:00" }, { "name": "matthiasmullie/path-converter", - "version": "1.1.0", + "version": "1.1.1", "source": { "type": "git", "url": "https://github.com/matthiasmullie/path-converter.git", - "reference": "08551ec1b156e923c242a10ab484bd4d6ead6631" + "reference": "3082a6838be02b930239a97d38b5c9da4d693aca" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/matthiasmullie/path-converter/zipball/08551ec1b156e923c242a10ab484bd4d6ead6631", - "reference": "08551ec1b156e923c242a10ab484bd4d6ead6631", + "url": "https://api.github.com/repos/matthiasmullie/path-converter/zipball/3082a6838be02b930239a97d38b5c9da4d693aca", + "reference": "3082a6838be02b930239a97d38b5c9da4d693aca", "shasum": "" }, "require": { @@ -696,26 +697,26 @@ "paths", "relative" ], - "time": "2017-01-26T08:54:49+00:00" + "time": "2018-02-02T11:30:10+00:00" }, { "name": "maximebf/debugbar", - "version": "v1.14.1", + "version": "v1.15.0", "source": { "type": "git", "url": "https://github.com/maximebf/php-debugbar.git", - "reference": "64251a392344e3d22f3d21c3b7c531ba96eb01d2" + "reference": "30e7d60937ee5f1320975ca9bc7bcdd44d500f07" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/maximebf/php-debugbar/zipball/64251a392344e3d22f3d21c3b7c531ba96eb01d2", - "reference": "64251a392344e3d22f3d21c3b7c531ba96eb01d2", + "url": "https://api.github.com/repos/maximebf/php-debugbar/zipball/30e7d60937ee5f1320975ca9bc7bcdd44d500f07", + "reference": "30e7d60937ee5f1320975ca9bc7bcdd44d500f07", "shasum": "" }, "require": { "php": ">=5.3.0", "psr/log": "^1.0", - "symfony/var-dumper": "^2.6|^3.0" + "symfony/var-dumper": "^2.6|^3.0|^4.0" }, "require-dev": { "phpunit/phpunit": "^4.0|^5.0" @@ -757,7 +758,7 @@ "debug", "debugbar" ], - "time": "2017-09-13T12:19:36+00:00" + "time": "2017-12-15T11:13:46+00:00" }, { "name": "miljar/php-exif", @@ -894,16 +895,16 @@ }, { "name": "pimple/pimple", - "version": "v3.2.2", + "version": "v3.2.3", "source": { "type": "git", "url": "https://github.com/silexphp/Pimple.git", - "reference": "4d45fb62d96418396ec58ba76e6f065bca16e10a" + "reference": "9e403941ef9d65d20cba7d54e29fe906db42cf32" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/silexphp/Pimple/zipball/4d45fb62d96418396ec58ba76e6f065bca16e10a", - "reference": "4d45fb62d96418396ec58ba76e6f065bca16e10a", + "url": "https://api.github.com/repos/silexphp/Pimple/zipball/9e403941ef9d65d20cba7d54e29fe906db42cf32", + "reference": "9e403941ef9d65d20cba7d54e29fe906db42cf32", "shasum": "" }, "require": { @@ -940,7 +941,7 @@ "container", "dependency injection" ], - "time": "2017-07-23T07:32:15+00:00" + "time": "2018-01-21T07:42:36+00:00" }, { "name": "psr/container", @@ -1038,23 +1039,73 @@ ], "time": "2016-10-10T12:19:37+00:00" }, + { + "name": "psr/simple-cache", + "version": "1.0.1", + "source": { + "type": "git", + "url": "https://github.com/php-fig/simple-cache.git", + "reference": "408d5eafb83c57f6365a3ca330ff23aa4a5fa39b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/simple-cache/zipball/408d5eafb83c57f6365a3ca330ff23aa4a5fa39b", + "reference": "408d5eafb83c57f6365a3ca330ff23aa4a5fa39b", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\SimpleCache\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common interfaces for simple caching", + "keywords": [ + "cache", + "caching", + "psr", + "psr-16", + "simple-cache" + ], + "time": "2017-10-23T01:57:42+00:00" + }, { "name": "roave/security-advisories", "version": "dev-master", "source": { "type": "git", "url": "https://github.com/Roave/SecurityAdvisories.git", - "reference": "40b035345ed34a4cc92c842f60a6cc739101542f" + "reference": "664836e89c7ecad3dbaabc1572ea752c0d532d80" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/40b035345ed34a4cc92c842f60a6cc739101542f", - "reference": "40b035345ed34a4cc92c842f60a6cc739101542f", + "url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/664836e89c7ecad3dbaabc1572ea752c0d532d80", + "reference": "664836e89c7ecad3dbaabc1572ea752c0d532d80", "shasum": "" }, "conflict": { + "3f/pygmentize": "<1.2", "adodb/adodb-php": "<5.20.6", "amphp/artax": "<1.0.6|>=2,<2.0.6", + "asymmetricrypt/asymmetricrypt": ">=0,<9.9.99", "aws/aws-sdk-php": ">=3,<3.2.1", "bugsnag/bugsnag-laravel": ">=2,<2.0.2", "cakephp/cakephp": ">=1.3,<1.3.18|>=2,<2.4.99|>=2.5,<2.5.99|>=2.6,<2.6.12|>=2.7,<2.7.6|>=3,<3.0.15|>=3.1,<3.1.4", @@ -1063,9 +1114,10 @@ "codeigniter/framework": "<=3.0.6", "composer/composer": "<=1.0.0-alpha11", "contao-components/mediaelement": ">=2.14.2,<2.21.1", - "contao/core": ">=2,<3.5.31", + "contao/core": ">=2,<3.5.32", "contao/core-bundle": ">=4,<4.4.8", "contao/listing-bundle": ">=4,<4.4.8", + "contao/newsletter-bundle": ">=4,<4.1", "doctrine/annotations": ">=1,<1.2.7", "doctrine/cache": ">=1,<1.3.2|>=1.4,<1.4.2", "doctrine/common": ">=2,<2.4.3|>=2.5,<2.5.1", @@ -1076,9 +1128,10 @@ "doctrine/mongodb-odm-bundle": ">=2,<3.0.1", "doctrine/orm": ">=2,<2.4.8|>=2.5,<2.5.1", "dompdf/dompdf": ">=0.6,<0.6.2", - "drupal/core": ">=8,<8.3.7", - "drupal/drupal": ">=8,<8.3.7", - "ezsystems/ezpublish-legacy": ">=5.3,<5.3.12.2|>=5.4,<5.4.10.1|>=2017.8,<2017.8.1.1", + "drupal/core": ">=8,<8.4.5", + "drupal/drupal": ">=8,<8.4.5", + "erusev/parsedown": "<1.7", + "ezsystems/ezpublish-legacy": ">=5.3,<5.3.12.3|>=5.4,<5.4.11.3|>=2017.8,<2017.8.1.1|>=2017.12,<2017.12.2.1", "firebase/php-jwt": "<2", "friendsofsymfony/rest-bundle": ">=1.2,<1.2.2", "friendsofsymfony/user-bundle": ">=1.2,<1.3.5", @@ -1098,21 +1151,27 @@ "onelogin/php-saml": "<2.10.4", "oro/crm": ">=1.7,<1.7.4", "oro/platform": ">=1.7,<1.7.4", + "padraic/humbug_get_contents": "<1.1.2", + "pagarme/pagarme-php": ">=0,<3", + "paragonie/random_compat": "<2", "phpmailer/phpmailer": ">=5,<5.2.24", "phpunit/phpunit": ">=4.8.19,<4.8.28|>=5.0.10,<5.6.3", - "phpxmlrpc/extras": "<6.0.1", + "phpxmlrpc/extras": "<0.6.1", + "propel/propel": ">=2.0.0-alpha1,<=2.0.0-alpha7", + "propel/propel1": ">=1,<=1.7.1", "pusher/pusher-php-server": "<2.2.1", "sabre/dav": ">=1.6,<1.6.99|>=1.7,<1.7.11|>=1.8,<1.8.9", - "shopware/shopware": "<5.2.25", + "shopware/shopware": "<5.3.7", "silverstripe/cms": ">=3,<=3.0.11|>=3.1,<3.1.11", "silverstripe/forum": "<=0.6.1|>=0.7,<=0.7.3", "silverstripe/framework": ">=3,<3.3", "silverstripe/userforms": "<3", - "simplesamlphp/saml2": "<1.8.1|>=1.9,<1.9.1|>=1.10,<1.10.3|>=2,<2.3.3", - "simplesamlphp/simplesamlphp": "<1.14.16", + "simplesamlphp/saml2": "<1.10.6|>=2,<2.3.8|>=3,<3.1.4", + "simplesamlphp/simplesamlphp": "<1.15.2", "simplesamlphp/simplesamlphp-module-infocard": "<1.0.1", "socalnick/scn-social-auth": "<1.15.2", "squizlabs/php_codesniffer": ">=1,<2.8.1", + "stormpath/sdk": ">=0,<9.9.99", "swiftmailer/swiftmailer": ">=4,<5.4.5", "symfony/dependency-injection": ">=2,<2.0.17", "symfony/form": ">=2.3,<2.3.35|>=2.4,<2.6.12|>=2.7,<2.7.38|>=2.8,<2.8.31|>=3,<3.2.14|>=3.3,<3.3.13", @@ -1133,15 +1192,16 @@ "symfony/yaml": ">=2,<2.0.22|>=2.1,<2.1.7", "thelia/backoffice-default-template": ">=2.1,<2.1.2", "thelia/thelia": ">=2.1,<2.1.2|>=2.1.0-beta1,<2.1.3", + "titon/framework": ">=0,<9.9.99", "twig/twig": "<1.20", "typo3/cms": ">=6.2,<6.2.30|>=7,<7.6.22|>=8,<8.7.5", "typo3/flow": ">=1,<1.0.4|>=1.1,<1.1.1|>=2,<2.0.1|>=2.3,<2.3.16|>=3,<3.0.10|>=3.1,<3.1.7|>=3.2,<3.2.7|>=3.3,<3.3.5", "typo3/neos": ">=1.1,<1.1.3|>=1.2,<1.2.13|>=2,<2.0.4", "willdurand/js-translation-bundle": "<2.1.1", "yiisoft/yii": ">=1.1.14,<1.1.15", - "yiisoft/yii2": "<2.0.5", + "yiisoft/yii2": "<2.0.14", "yiisoft/yii2-bootstrap": "<2.0.4", - "yiisoft/yii2-dev": "<2.0.4", + "yiisoft/yii2-dev": "<2.0.14", "yiisoft/yii2-gii": "<2.0.4", "yiisoft/yii2-jui": "<2.0.4", "zendframework/zend-cache": ">=2.4,<2.4.8|>=2.5,<2.5.3", @@ -1181,20 +1241,20 @@ } ], "description": "Prevents installation of composer packages with known security vulnerabilities: no API, simply require it", - "time": "2017-12-12T00:38:43+00:00" + "time": "2018-03-07T15:45:44+00:00" }, { "name": "rockettheme/toolbox", - "version": "1.3.8", + "version": "1.3.9", "source": { "type": "git", "url": "https://github.com/rockettheme/toolbox.git", - "reference": "2f7c83780bbcf527e057f15efe755e10694eb2bc" + "reference": "1deea4b45e09f6e0c3e6e075e175e50d05ccbf70" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/rockettheme/toolbox/zipball/2f7c83780bbcf527e057f15efe755e10694eb2bc", - "reference": "2f7c83780bbcf527e057f15efe755e10694eb2bc", + "url": "https://api.github.com/repos/rockettheme/toolbox/zipball/1deea4b45e09f6e0c3e6e075e175e50d05ccbf70", + "reference": "1deea4b45e09f6e0c3e6e075e175e50d05ccbf70", "shasum": "" }, "require": { @@ -1229,7 +1289,7 @@ "php", "rockettheme" ], - "time": "2017-09-23T13:39:25+00:00" + "time": "2018-03-09T00:03:18+00:00" }, { "name": "seld/cli-prompt", @@ -1281,16 +1341,16 @@ }, { "name": "symfony/console", - "version": "v2.8.32", + "version": "v2.8.36", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "46270f1ca44f08ebc134ce120fd2c2baf5fd63de" + "reference": "a6ff8b2ffa4eb43046828b303af2e3fedadacc27" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/46270f1ca44f08ebc134ce120fd2c2baf5fd63de", - "reference": "46270f1ca44f08ebc134ce120fd2c2baf5fd63de", + "url": "https://api.github.com/repos/symfony/console/zipball/a6ff8b2ffa4eb43046828b303af2e3fedadacc27", + "reference": "a6ff8b2ffa4eb43046828b303af2e3fedadacc27", "shasum": "" }, "require": { @@ -1338,7 +1398,7 @@ ], "description": "Symfony Console Component", "homepage": "https://symfony.com", - "time": "2017-11-29T09:33:18+00:00" + "time": "2018-02-26T15:33:21+00:00" }, { "name": "symfony/debug", @@ -1399,16 +1459,16 @@ }, { "name": "symfony/event-dispatcher", - "version": "v2.8.32", + "version": "v2.8.36", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "b59aacf238fadda50d612c9de73b74751872a903" + "reference": "f5d2d7dcc33b89e20c2696ea9afcbddf6540081c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/b59aacf238fadda50d612c9de73b74751872a903", - "reference": "b59aacf238fadda50d612c9de73b74751872a903", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/f5d2d7dcc33b89e20c2696ea9afcbddf6540081c", + "reference": "f5d2d7dcc33b89e20c2696ea9afcbddf6540081c", "shasum": "" }, "require": { @@ -1455,20 +1515,20 @@ ], "description": "Symfony EventDispatcher Component", "homepage": "https://symfony.com", - "time": "2017-11-05T15:25:56+00:00" + "time": "2018-02-11T16:53:59+00:00" }, { "name": "symfony/polyfill-iconv", - "version": "v1.6.0", + "version": "v1.7.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-iconv.git", - "reference": "7a84ccdb8c953ee274c96dd6bde778d873fc824a" + "reference": "bd515d8f392730c833bc1ba993a4f598da64fa5b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-iconv/zipball/7a84ccdb8c953ee274c96dd6bde778d873fc824a", - "reference": "7a84ccdb8c953ee274c96dd6bde778d873fc824a", + "url": "https://api.github.com/repos/symfony/polyfill-iconv/zipball/bd515d8f392730c833bc1ba993a4f598da64fa5b", + "reference": "bd515d8f392730c833bc1ba993a4f598da64fa5b", "shasum": "" }, "require": { @@ -1480,7 +1540,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.6-dev" + "dev-master": "1.7-dev" } }, "autoload": { @@ -1514,20 +1574,20 @@ "portable", "shim" ], - "time": "2017-10-11T12:05:26+00:00" + "time": "2018-01-30T19:27:44+00:00" }, { "name": "symfony/polyfill-mbstring", - "version": "v1.6.0", + "version": "v1.7.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "2ec8b39c38cb16674bbf3fea2b6ce5bf117e1296" + "reference": "78be803ce01e55d3491c1397cf1c64beb9c1b63b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/2ec8b39c38cb16674bbf3fea2b6ce5bf117e1296", - "reference": "2ec8b39c38cb16674bbf3fea2b6ce5bf117e1296", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/78be803ce01e55d3491c1397cf1c64beb9c1b63b", + "reference": "78be803ce01e55d3491c1397cf1c64beb9c1b63b", "shasum": "" }, "require": { @@ -1539,7 +1599,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.6-dev" + "dev-master": "1.7-dev" } }, "autoload": { @@ -1573,20 +1633,20 @@ "portable", "shim" ], - "time": "2017-10-11T12:05:26+00:00" + "time": "2018-01-30T19:27:44+00:00" }, { "name": "symfony/var-dumper", - "version": "v2.8.32", + "version": "v2.8.36", "source": { "type": "git", "url": "https://github.com/symfony/var-dumper.git", - "reference": "0f2d237b01db1a1a486e330ad89be836513e19a0" + "reference": "e6a3e8c832096f8902a3e80169fca5d202e7e0d3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-dumper/zipball/0f2d237b01db1a1a486e330ad89be836513e19a0", - "reference": "0f2d237b01db1a1a486e330ad89be836513e19a0", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/e6a3e8c832096f8902a3e80169fca5d202e7e0d3", + "reference": "e6a3e8c832096f8902a3e80169fca5d202e7e0d3", "shasum": "" }, "require": { @@ -1641,20 +1701,20 @@ "debug", "dump" ], - "time": "2017-11-07T14:08:47+00:00" + "time": "2018-01-31T10:36:06+00:00" }, { "name": "symfony/yaml", - "version": "v2.8.32", + "version": "v2.8.36", "source": { "type": "git", "url": "https://github.com/symfony/yaml.git", - "reference": "968ef42161e4bc04200119da473077f9e7015128" + "reference": "be720fcfae4614df204190d57795351059946a77" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/968ef42161e4bc04200119da473077f9e7015128", - "reference": "968ef42161e4bc04200119da473077f9e7015128", + "url": "https://api.github.com/repos/symfony/yaml/zipball/be720fcfae4614df204190d57795351059946a77", + "reference": "be720fcfae4614df204190d57795351059946a77", "shasum": "" }, "require": { @@ -1690,20 +1750,20 @@ ], "description": "Symfony Yaml Component", "homepage": "https://symfony.com", - "time": "2017-11-29T09:33:18+00:00" + "time": "2018-01-03T07:36:31+00:00" }, { "name": "twig/twig", - "version": "v1.35.0", + "version": "v1.35.2", "source": { "type": "git", "url": "https://github.com/twigphp/Twig.git", - "reference": "daa657073e55b0a78cce8fdd22682fddecc6385f" + "reference": "9c24f2cd39dc1906b76879e099970b7e53724601" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/twigphp/Twig/zipball/daa657073e55b0a78cce8fdd22682fddecc6385f", - "reference": "daa657073e55b0a78cce8fdd22682fddecc6385f", + "url": "https://api.github.com/repos/twigphp/Twig/zipball/9c24f2cd39dc1906b76879e099970b7e53724601", + "reference": "9c24f2cd39dc1906b76879e099970b7e53724601", "shasum": "" }, "require": { @@ -1755,22 +1815,22 @@ "keywords": [ "templating" ], - "time": "2017-09-27T18:06:46+00:00" + "time": "2018-03-03T16:21:29+00:00" } ], "packages-dev": [ { "name": "behat/gherkin", - "version": "v4.4.5", + "version": "v4.5.1", "source": { "type": "git", "url": "https://github.com/Behat/Gherkin.git", - "reference": "5c14cff4f955b17d20d088dec1bde61c0539ec74" + "reference": "74ac03d52c5e23ad8abd5c5cce4ab0e8dc1b530a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Behat/Gherkin/zipball/5c14cff4f955b17d20d088dec1bde61c0539ec74", - "reference": "5c14cff4f955b17d20d088dec1bde61c0539ec74", + "url": "https://api.github.com/repos/Behat/Gherkin/zipball/74ac03d52c5e23ad8abd5c5cce4ab0e8dc1b530a", + "reference": "74ac03d52c5e23ad8abd5c5cce4ab0e8dc1b530a", "shasum": "" }, "require": { @@ -1816,35 +1876,32 @@ "gherkin", "parser" ], - "time": "2016-10-30T11:50:56+00:00" + "time": "2017-08-30T11:04:43+00:00" }, { "name": "codeception/codeception", - "version": "2.3.7", + "version": "2.4.0", "source": { "type": "git", "url": "https://github.com/Codeception/Codeception.git", - "reference": "151de88277878adc18784ef3eaddd87f4a2fdc14" + "reference": "c50789a9a62cc0eefc0252e88a5f04f8c47b55f4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Codeception/Codeception/zipball/151de88277878adc18784ef3eaddd87f4a2fdc14", - "reference": "151de88277878adc18784ef3eaddd87f4a2fdc14", + "url": "https://api.github.com/repos/Codeception/Codeception/zipball/c50789a9a62cc0eefc0252e88a5f04f8c47b55f4", + "reference": "c50789a9a62cc0eefc0252e88a5f04f8c47b55f4", "shasum": "" }, "require": { - "behat/gherkin": "~4.4.0", + "behat/gherkin": "^4.4.0", + "codeception/phpunit-wrapper": "^6.0|^7.0", + "codeception/stub": "^1.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", - "phpunit/php-code-coverage": ">=2.2.4 <6.0", - "phpunit/phpunit": ">=4.8.28 <5.0.0 || >=5.6.3 <7.0", - "phpunit/phpunit-mock-objects": ">2.3 <5.0", - "sebastian/comparator": ">1.1 <3.0", - "sebastian/diff": ">=1.4 <3.0", "symfony/browser-kit": ">=2.7 <5.0", "symfony/console": ">=2.7 <5.0", "symfony/css-selector": ">=2.7 <5.0", @@ -1857,8 +1914,6 @@ "codeception/specify": "~0.3", "facebook/graph-sdk": "~5.3", "flow/jsonpath": "~0.2", - "league/factory-muffin": "^3.0", - "league/factory-muffin-faker": "^1.0", "monolog/monolog": "~1.8", "pda/pheanstalk": "~3.0", "php-amqplib/php-amqplib": "~2.4", @@ -1912,7 +1967,79 @@ "functional testing", "unit testing" ], - "time": "2017-12-12T04:22:17+00:00" + "time": "2018-02-27T00:09:12+00:00" + }, + { + "name": "codeception/phpunit-wrapper", + "version": "6.0.5", + "source": { + "type": "git", + "url": "https://github.com/Codeception/phpunit-wrapper.git", + "reference": "44e2100c300413a6b40cf8874ad402695010f443" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Codeception/phpunit-wrapper/zipball/44e2100c300413a6b40cf8874ad402695010f443", + "reference": "44e2100c300413a6b40cf8874ad402695010f443", + "shasum": "" + }, + "require": { + "phpunit/php-code-coverage": ">=2.2.4 <6.0", + "phpunit/phpunit": ">=4.8.28 <5.0.0 || >=5.6.3 <7.0", + "sebastian/comparator": ">1.1 <3.0", + "sebastian/diff": ">=1.4 <4.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Codeception\\PHPUnit\\": "src\\" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Davert", + "email": "davert.php@resend.cc" + } + ], + "description": "PHPUnit classes used by Codeception", + "time": "2018-02-19T13:24:40+00:00" + }, + { + "name": "codeception/stub", + "version": "1.0.2", + "source": { + "type": "git", + "url": "https://github.com/Codeception/Stub.git", + "reference": "95fb7a36b81890dd2e5163e7ab31310df6f1bb99" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Codeception/Stub/zipball/95fb7a36b81890dd2e5163e7ab31310df6f1bb99", + "reference": "95fb7a36b81890dd2e5163e7ab31310df6f1bb99", + "shasum": "" + }, + "require": { + "phpunit/phpunit-mock-objects": ">2.3 <7.0" + }, + "require-dev": { + "phpunit/phpunit": ">=4.8 <8.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Codeception\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Flexible Stub wrapper for PHPUnit's Mock Builder", + "time": "2018-02-18T13:56:56+00:00" }, { "name": "doctrine/instantiator", @@ -2399,16 +2526,16 @@ }, { "name": "phpspec/prophecy", - "version": "1.7.3", + "version": "1.7.5", "source": { "type": "git", "url": "https://github.com/phpspec/prophecy.git", - "reference": "e4ed002c67da8eceb0eb8ddb8b3847bb53c5c2bf" + "reference": "dfd6be44111a7c41c2e884a336cc4f461b3b2401" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpspec/prophecy/zipball/e4ed002c67da8eceb0eb8ddb8b3847bb53c5c2bf", - "reference": "e4ed002c67da8eceb0eb8ddb8b3847bb53c5c2bf", + "url": "https://api.github.com/repos/phpspec/prophecy/zipball/dfd6be44111a7c41c2e884a336cc4f461b3b2401", + "reference": "dfd6be44111a7c41c2e884a336cc4f461b3b2401", "shasum": "" }, "require": { @@ -2420,7 +2547,7 @@ }, "require-dev": { "phpspec/phpspec": "^2.5|^3.2", - "phpunit/phpunit": "^4.8.35 || ^5.7" + "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.5" }, "type": "library", "extra": { @@ -2458,7 +2585,7 @@ "spy", "stub" ], - "time": "2017-11-24T13:59:53+00:00" + "time": "2018-02-19T10:16:54+00:00" }, { "name": "phpunit/php-code-coverage", @@ -3260,16 +3387,16 @@ }, { "name": "symfony/browser-kit", - "version": "v3.4.1", + "version": "v3.4.6", "source": { "type": "git", "url": "https://github.com/symfony/browser-kit.git", - "reference": "179522b5f0b5e6d00bb60f38a4d6b29962e4b61b" + "reference": "490f27762705c8489bd042fe3e9377a191dba9b4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/browser-kit/zipball/179522b5f0b5e6d00bb60f38a4d6b29962e4b61b", - "reference": "179522b5f0b5e6d00bb60f38a4d6b29962e4b61b", + "url": "https://api.github.com/repos/symfony/browser-kit/zipball/490f27762705c8489bd042fe3e9377a191dba9b4", + "reference": "490f27762705c8489bd042fe3e9377a191dba9b4", "shasum": "" }, "require": { @@ -3313,20 +3440,20 @@ ], "description": "Symfony BrowserKit Component", "homepage": "https://symfony.com", - "time": "2017-11-07T14:20:24+00:00" + "time": "2018-01-03T07:37:34+00:00" }, { "name": "symfony/css-selector", - "version": "v3.4.1", + "version": "v3.4.6", "source": { "type": "git", "url": "https://github.com/symfony/css-selector.git", - "reference": "7134b93e90ea7e7881fcb2da006d21b4c5f31908" + "reference": "544655f1fc078a9cd839fdda2b7b1e64627c826a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/css-selector/zipball/7134b93e90ea7e7881fcb2da006d21b4c5f31908", - "reference": "7134b93e90ea7e7881fcb2da006d21b4c5f31908", + "url": "https://api.github.com/repos/symfony/css-selector/zipball/544655f1fc078a9cd839fdda2b7b1e64627c826a", + "reference": "544655f1fc078a9cd839fdda2b7b1e64627c826a", "shasum": "" }, "require": { @@ -3366,20 +3493,20 @@ ], "description": "Symfony CssSelector Component", "homepage": "https://symfony.com", - "time": "2017-11-05T16:10:10+00:00" + "time": "2018-02-03T14:55:07+00:00" }, { "name": "symfony/dom-crawler", - "version": "v3.4.1", + "version": "v3.4.6", "source": { "type": "git", "url": "https://github.com/symfony/dom-crawler.git", - "reference": "7bf68716e400997a291ad42c9f9fe7972e6656d2" + "reference": "2bb5d3101cc01f4fe580e536daf4f1959bc2d24d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/7bf68716e400997a291ad42c9f9fe7972e6656d2", - "reference": "7bf68716e400997a291ad42c9f9fe7972e6656d2", + "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/2bb5d3101cc01f4fe580e536daf4f1959bc2d24d", + "reference": "2bb5d3101cc01f4fe580e536daf4f1959bc2d24d", "shasum": "" }, "require": { @@ -3422,20 +3549,20 @@ ], "description": "Symfony DomCrawler Component", "homepage": "https://symfony.com", - "time": "2017-11-05T16:10:10+00:00" + "time": "2018-02-22T10:48:49+00:00" }, { "name": "symfony/finder", - "version": "v3.4.1", + "version": "v3.4.6", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "dac8d7db537bac7ad8143eb11360a8c2231f251a" + "reference": "a479817ce0a9e4adfd7d39c6407c95d97c254625" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/dac8d7db537bac7ad8143eb11360a8c2231f251a", - "reference": "dac8d7db537bac7ad8143eb11360a8c2231f251a", + "url": "https://api.github.com/repos/symfony/finder/zipball/a479817ce0a9e4adfd7d39c6407c95d97c254625", + "reference": "a479817ce0a9e4adfd7d39c6407c95d97c254625", "shasum": "" }, "require": { @@ -3471,20 +3598,20 @@ ], "description": "Symfony Finder Component", "homepage": "https://symfony.com", - "time": "2017-11-05T16:10:10+00:00" + "time": "2018-03-05T18:28:11+00:00" }, { "name": "symfony/process", - "version": "v3.4.1", + "version": "v3.4.6", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "db25e810fd5e124085e3777257d0cf4ae533d0ea" + "reference": "cc4aea21f619116aaf1c58016a944e4821c8e8af" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/db25e810fd5e124085e3777257d0cf4ae533d0ea", - "reference": "db25e810fd5e124085e3777257d0cf4ae533d0ea", + "url": "https://api.github.com/repos/symfony/process/zipball/cc4aea21f619116aaf1c58016a944e4821c8e8af", + "reference": "cc4aea21f619116aaf1c58016a944e4821c8e8af", "shasum": "" }, "require": { @@ -3520,7 +3647,7 @@ ], "description": "Symfony Process Component", "homepage": "https://symfony.com", - "time": "2017-11-22T12:18:49+00:00" + "time": "2018-02-12T17:55:00+00:00" }, { "name": "victorjonsson/markdowndocs", @@ -3570,16 +3697,16 @@ }, { "name": "webmozart/assert", - "version": "1.2.0", + "version": "1.3.0", "source": { "type": "git", "url": "https://github.com/webmozart/assert.git", - "reference": "2db61e59ff05fe5126d152bd0655c9ea113e550f" + "reference": "0df1908962e7a3071564e857d86874dad1ef204a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/webmozart/assert/zipball/2db61e59ff05fe5126d152bd0655c9ea113e550f", - "reference": "2db61e59ff05fe5126d152bd0655c9ea113e550f", + "url": "https://api.github.com/repos/webmozart/assert/zipball/0df1908962e7a3071564e857d86874dad1ef204a", + "reference": "0df1908962e7a3071564e857d86874dad1ef204a", "shasum": "" }, "require": { @@ -3616,7 +3743,7 @@ "check", "validate" ], - "time": "2016-11-23T20:04:58+00:00" + "time": "2018-01-29T19:49:41+00:00" } ], "aliases": [], diff --git a/index.php b/index.php index 169eed811..008212503 100644 --- a/index.php +++ b/index.php @@ -2,7 +2,7 @@ /** * @package Grav.Core * - * @copyright Copyright (C) 2014 - 2017 RocketTheme, LLC. All rights reserved. + * @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/assets/whoops.css b/system/assets/whoops.css index c50aae385..18453b108 100644 --- a/system/assets/whoops.css +++ b/system/assets/whoops.css @@ -1,5 +1,5 @@ body header { - background: #8552A2; + background: #3085EE; } body .left-panel { diff --git a/system/blueprints/pages/default.yaml b/system/blueprints/pages/default.yaml index 8472b3b1a..8f81e527f 100644 --- a/system/blueprints/pages/default.yaml +++ b/system/blueprints/pages/default.yaml @@ -310,6 +310,31 @@ form: toggleable: true help: PLUGIN_ADMIN.APPEND_URL_EXT_HELP + routes_only: + type: section + title: PLUGIN_ADMIN.ROUTE_OVERRIDES + underline: true + + fields: + + header.routes.default: + type: text + toggleable: true + label: PLUGIN_ADMIN.ROUTE_DEFAULT + + header.routes.canonical: + type: text + toggleable: true + label: PLUGIN_ADMIN.ROUTE_CANONICAL + + header.routes.aliases: + type: array + toggleable: true + value_only: true + size: large + label: PLUGIN_ADMIN.ROUTE_ALIASES + + admin_only: type: section title: PLUGIN_ADMIN.ADMIN_SPECIFIC_OVERRIDES diff --git a/system/blueprints/pages/modular.yaml b/system/blueprints/pages/modular.yaml index 76a60f8c7..89b107a9d 100644 --- a/system/blueprints/pages/modular.yaml +++ b/system/blueprints/pages/modular.yaml @@ -1,7 +1,5 @@ title: PLUGIN_ADMIN.MODULAR -@extends: - type: default - context: blueprints://pages +'@extends': default form: fields: @@ -13,35 +11,28 @@ form: content: fields: + modular_title: + type: spacer + title: Modular Setup + header.content.items: - type: select + type: text label: PLUGIN_ADMIN.ITEMS default: '@self.modular' - options: - '@self.modular': Modular Children + size: medium header.content.order.by: - type: select + type: text label: PLUGIN_ADMIN.ORDER_BY - default: date - options: - folder: PLUGIN_ADMIN.FOLDER - title: PLUGIN_ADMIN.TITLE - date: PLUGIN_ADMIN.DATE - default: PLUGIN_ADMIN.DEFAULT + placeholder: date + help: + size: small header.content.order.dir: - type: select + type: text label: PLUGIN_ADMIN.ORDER - default: desc - options: - asc: PLUGIN_ADMIN.ASCENDING - desc: PLUGIN_ADMIN.DESCENDING - - header.process: - type: ignore - content: - type: ignore - header.media_order: - type: ignore + help: '"desc" or "asc" are valid values' + placeholder: desc + size: small + diff --git a/system/blueprints/user/account.yaml b/system/blueprints/user/account.yaml index 29628c40a..7717a01bb 100644 --- a/system/blueprints/user/account.yaml +++ b/system/blueprints/user/account.yaml @@ -40,6 +40,7 @@ form: type: password size: large label: PLUGIN_ADMIN.PASSWORD + autocomplete: new-password validate: required: false message: PLUGIN_ADMIN.PASSWORD_VALIDATION_MESSAGE diff --git a/system/config/system.yaml b/system/config/system.yaml index 72c6bc2ff..1416c92e2 100644 --- a/system/config/system.yaml +++ b/system/config/system.yaml @@ -132,6 +132,7 @@ media: session: enabled: true # Enable Session support + initialize: true # Initialize session from Grav (if false, plugin needs to start the session) timeout: 1800 # Timeout in seconds name: grav-site # Name prefix of the session cookie. Use alphanumeric, dashes or underscores only. Do not use dots in the session name secure: false # Set session secure. If true, indicates that communication for this cookie must be over an encrypted transmission. Enable this only on sites that run exclusively on HTTPS diff --git a/system/defines.php b/system/defines.php index 1892be1a9..08054822e 100644 --- a/system/defines.php +++ b/system/defines.php @@ -2,14 +2,14 @@ /** * @package Grav.Core * - * @copyright Copyright (C) 2014 - 2017 RocketTheme, LLC. All rights reserved. + * @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ // Some standard defines define('GRAV', true); -define('GRAV_VERSION', '1.3.10'); -//define('GRAV_TESTING', true); +define('GRAV_VERSION', '1.4.0-rc.2'); +define('GRAV_TESTING', true); define('DS', '/'); if (!defined('GRAV_PHP_MIN')) { diff --git a/system/router.php b/system/router.php index 850ac1771..2e5af825e 100644 --- a/system/router.php +++ b/system/router.php @@ -2,7 +2,7 @@ /** * @package Grav.Core * - * @copyright Copyright (C) 2014 - 2017 RocketTheme, LLC. All rights reserved. + * @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Assets.php b/system/src/Grav/Common/Assets.php index d55d229c3..44e07fcd9 100644 --- a/system/src/Grav/Common/Assets.php +++ b/system/src/Grav/Common/Assets.php @@ -2,7 +2,7 @@ /** * @package Grav.Common * - * @copyright Copyright (C) 2014 - 2017 RocketTheme, LLC. All rights reserved. + * @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Backup/ZipBackup.php b/system/src/Grav/Common/Backup/ZipBackup.php index 2fef157e8..517d7da4d 100644 --- a/system/src/Grav/Common/Backup/ZipBackup.php +++ b/system/src/Grav/Common/Backup/ZipBackup.php @@ -2,7 +2,7 @@ /** * @package Grav.Common.Backup * - * @copyright Copyright (C) 2014 - 2017 RocketTheme, LLC. All rights reserved. + * @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -32,7 +32,7 @@ class ZipBackup /** * Backup * - * @param null $destination + * @param string|null $destination * @param callable|null $messager * * @return null|string @@ -107,18 +107,19 @@ public static function backup($destination = null, callable $messager = null) * @param $exclusiveLength * @param $messager */ - private static function folderToZip($folder, \ZipArchive &$zipFile, $exclusiveLength, callable $messager = null) + private static function folderToZip($folder, \ZipArchive $zipFile, $exclusiveLength, callable $messager = null) { $handle = opendir($folder); while (false !== $f = readdir($handle)) { - if ($f != '.' && $f != '..') { + if ($f !== '.' && $f !== '..') { $filePath = "$folder/$f"; // Remove prefix from file path before add to zip. $localPath = substr($filePath, $exclusiveLength); if (in_array($f, static::$ignoreFolders)) { continue; - } elseif (in_array($localPath, static::$ignorePaths)) { + } + if (in_array($localPath, static::$ignorePaths)) { $zipFile->addEmptyDir($f); continue; } diff --git a/system/src/Grav/Common/Browser.php b/system/src/Grav/Common/Browser.php index 3a8417c4d..bb0a3fb4f 100644 --- a/system/src/Grav/Common/Browser.php +++ b/system/src/Grav/Common/Browser.php @@ -2,7 +2,7 @@ /** * @package Grav.Common * - * @copyright Copyright (C) 2014 - 2017 RocketTheme, LLC. All rights reserved. + * @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Cache.php b/system/src/Grav/Common/Cache.php index 8e9ed3977..71eb29ff7 100644 --- a/system/src/Grav/Common/Cache.php +++ b/system/src/Grav/Common/Cache.php @@ -2,7 +2,7 @@ /** * @package Grav.Common * - * @copyright Copyright (C) 2014 - 2017 RocketTheme, LLC. All rights reserved. + * @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -429,6 +429,14 @@ public static function clearCache($remove = 'standard') $output[] = ''; } + // Clear stat cache + @clearstatcache(); + + // Clear opcache + if (function_exists('opcache_reset')) { + @opcache_reset(); + } + return $output; } diff --git a/system/src/Grav/Common/Composer.php b/system/src/Grav/Common/Composer.php index 9c65fd69b..26719252c 100644 --- a/system/src/Grav/Common/Composer.php +++ b/system/src/Grav/Common/Composer.php @@ -2,7 +2,7 @@ /** * @package Grav.Common * - * @copyright Copyright (C) 2014 - 2017 RocketTheme, LLC. All rights reserved. + * @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Config/CompiledBase.php b/system/src/Grav/Common/Config/CompiledBase.php index b4e2abb29..1e6379599 100644 --- a/system/src/Grav/Common/Config/CompiledBase.php +++ b/system/src/Grav/Common/Config/CompiledBase.php @@ -2,7 +2,7 @@ /** * @package Grav.Common.Config * - * @copyright Copyright (C) 2014 - 2017 RocketTheme, LLC. All rights reserved. + * @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Config/CompiledBlueprints.php b/system/src/Grav/Common/Config/CompiledBlueprints.php index f827903c8..a29ecded9 100644 --- a/system/src/Grav/Common/Config/CompiledBlueprints.php +++ b/system/src/Grav/Common/Config/CompiledBlueprints.php @@ -2,7 +2,7 @@ /** * @package Grav.Common.Config * - * @copyright Copyright (C) 2014 - 2017 RocketTheme, LLC. All rights reserved. + * @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ namespace Grav\Common\Config; @@ -32,7 +32,7 @@ class CompiledBlueprints extends CompiledBase */ public function checksum() { - if (!isset($this->checksum)) { + if (null === $this->checksum) { $this->checksum = md5(json_encode($this->files) . json_encode($this->getTypes()) . $this->version); } @@ -92,6 +92,7 @@ protected function loadFiles() // Convert file list into parent list. $list = []; + /** @var array $files */ foreach ($this->files as $files) { foreach ($files as $name => $item) { $list[$name][] = $this->path . $item['file']; diff --git a/system/src/Grav/Common/Config/CompiledConfig.php b/system/src/Grav/Common/Config/CompiledConfig.php index 05d3ed9b1..6f21123aa 100644 --- a/system/src/Grav/Common/Config/CompiledConfig.php +++ b/system/src/Grav/Common/Config/CompiledConfig.php @@ -2,7 +2,7 @@ /** * @package Grav.Common.Config * - * @copyright Copyright (C) 2014 - 2017 RocketTheme, LLC. All rights reserved. + * @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Config/CompiledLanguages.php b/system/src/Grav/Common/Config/CompiledLanguages.php index cb572be44..610e347df 100644 --- a/system/src/Grav/Common/Config/CompiledLanguages.php +++ b/system/src/Grav/Common/Config/CompiledLanguages.php @@ -2,7 +2,7 @@ /** * @package Grav.Common.Config * - * @copyright Copyright (C) 2014 - 2017 RocketTheme, LLC. All rights reserved. + * @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -60,7 +60,7 @@ protected function loadFile($name, $filename) { $file = CompiledYamlFile::instance($filename); if (preg_match('|languages\.yaml$|', $filename)) { - $this->object->mergeRecursive($file->content()); + $this->object->mergeRecursive((array) $file->content()); } else { $this->object->mergeRecursive([$name => $file->content()]); } diff --git a/system/src/Grav/Common/Config/Config.php b/system/src/Grav/Common/Config/Config.php index 52921e6d0..55282cda4 100644 --- a/system/src/Grav/Common/Config/Config.php +++ b/system/src/Grav/Common/Config/Config.php @@ -2,7 +2,7 @@ /** * @package Grav.Common.Config * - * @copyright Copyright (C) 2014 - 2017 RocketTheme, LLC. All rights reserved. + * @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -16,6 +16,7 @@ class Config extends Data { + /** @var string */ protected $checksum; protected $modified = false; protected $timestamp = 0; diff --git a/system/src/Grav/Common/Config/ConfigFileFinder.php b/system/src/Grav/Common/Config/ConfigFileFinder.php index 8ff34274a..798067175 100644 --- a/system/src/Grav/Common/Config/ConfigFileFinder.php +++ b/system/src/Grav/Common/Config/ConfigFileFinder.php @@ -2,7 +2,7 @@ /** * @package Grav.Common.Config * - * @copyright Copyright (C) 2014 - 2017 RocketTheme, LLC. All rights reserved. + * @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Config/Languages.php b/system/src/Grav/Common/Config/Languages.php index badf07089..aa97cdbf7 100644 --- a/system/src/Grav/Common/Config/Languages.php +++ b/system/src/Grav/Common/Config/Languages.php @@ -2,7 +2,7 @@ /** * @package Grav.Common.Config * - * @copyright Copyright (C) 2014 - 2017 RocketTheme, LLC. All rights reserved. + * @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Config/Setup.php b/system/src/Grav/Common/Config/Setup.php index a8bbd544d..316b090d5 100644 --- a/system/src/Grav/Common/Config/Setup.php +++ b/system/src/Grav/Common/Config/Setup.php @@ -2,7 +2,7 @@ /** * @package Grav.Common.Config * - * @copyright Copyright (C) 2014 - 2017 RocketTheme, LLC. All rights reserved. + * @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -133,7 +133,7 @@ class Setup extends Data */ public function __construct($container) { - $environment = isset(static::$environment) ? static::$environment : ($container['uri']->environment() ?: 'localhost'); + $environment = null !== static::$environment ? static::$environment : ($container['uri']->environment() ?: 'localhost'); // Pre-load setup.php which contains our initial configuration. // Configuration may contain dynamic parts, which is why we need to always load it. @@ -151,11 +151,13 @@ public function __construct($container) // Set up environment. $this->def('environment', $environment ?: 'cli'); - $this->def('streams.schemes.environment.prefixes', ['' => ($environment ? ["user://{$this->environment}"] : [])]); + $this->def('streams.schemes.environment.prefixes', ['' => $environment ? ["user://{$this->environment}"] : []]); } /** * @return $this + * @throws \RuntimeException + * @throws \InvalidArgumentException */ public function init() { @@ -175,7 +177,7 @@ public function init() // Update streams. foreach (array_reverse($files) as $path) { $file = CompiledYamlFile::instance($path); - $content = $file->content(); + $content = (array)$file->content(); if (!empty($content['schemes'])) { $this->items['streams']['schemes'] = $content['schemes'] + $this->items['streams']['schemes']; } @@ -196,6 +198,7 @@ public function init() * Initialize resource locator by using the configuration. * * @param UniformResourceLocator $locator + * @throws \BadMethodCallException */ public function initializeLocator(UniformResourceLocator $locator) { @@ -212,7 +215,7 @@ public function initializeLocator(UniformResourceLocator $locator) $force = isset($config['force']) ? $config['force'] : false; if (isset($config['prefixes'])) { - foreach ($config['prefixes'] as $prefix => $paths) { + foreach ((array)$config['prefixes'] as $prefix => $paths) { $locator->addPath($scheme, $prefix, $paths, $override, $force); } } @@ -229,7 +232,7 @@ public function getStreams() $schemes = []; foreach ((array) $this->get('streams.schemes') as $scheme => $config) { $type = !empty($config['type']) ? $config['type'] : 'ReadOnlyStream'; - if ($type[0] != '\\') { + if ($type[0] !== '\\') { $type = '\\RocketTheme\\Toolbox\\StreamWrapper\\' . $type; } @@ -242,6 +245,8 @@ public function getStreams() /** * @param UniformResourceLocator $locator * @throws \InvalidArgumentException + * @throws \BadMethodCallException + * @throws \RuntimeException */ protected function check(UniformResourceLocator $locator) { diff --git a/system/src/Grav/Common/Data/Blueprint.php b/system/src/Grav/Common/Data/Blueprint.php index dc31f29ae..bf33d415f 100644 --- a/system/src/Grav/Common/Data/Blueprint.php +++ b/system/src/Grav/Common/Data/Blueprint.php @@ -2,7 +2,7 @@ /** * @package Grav.Common.Data * - * @copyright Copyright (C) 2014 - 2017 RocketTheme, LLC. All rights reserved. + * @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Data/BlueprintSchema.php b/system/src/Grav/Common/Data/BlueprintSchema.php index 8c621cae9..3205c3843 100644 --- a/system/src/Grav/Common/Data/BlueprintSchema.php +++ b/system/src/Grav/Common/Data/BlueprintSchema.php @@ -2,7 +2,7 @@ /** * @package Grav.Common.Data * - * @copyright Copyright (C) 2014 - 2017 RocketTheme, LLC. All rights reserved. + * @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -78,7 +78,7 @@ protected function validateArray(array $data, array $rules) } elseif (is_array($field) && is_array($val)) { // Array has been defined in blueprints. $messages += $this->validateArray($field, $val); - } elseif (isset($rules['validation']) && $rules['validation'] == 'strict') { + } elseif (isset($rules['validation']) && $rules['validation'] === 'strict') { // Undefined/extra item. throw new \RuntimeException(sprintf('%s is not defined in blueprints', $key)); } @@ -106,7 +106,7 @@ protected function filterArray(array $data, array $rules) } elseif (is_array($field) && is_array($val)) { // Array has been defined in blueprints. $field = $this->filterArray($field, $val); - } elseif (isset($rules['validation']) && $rules['validation'] == 'strict') { + } elseif (isset($rules['validation']) && $rules['validation'] === 'strict') { $field = null; } @@ -138,7 +138,7 @@ protected function checkRequired(array $data, array $fields) if (isset($data[$name])) { continue; } - if ($field['type'] == 'file' && isset($data['data']['name'][$name])) { //handle case of file input fields required + if ($field['type'] === 'file' && isset($data['data']['name'][$name])) { //handle case of file input fields required continue; } @@ -164,7 +164,7 @@ protected function dynamicConfig(array &$field, $property, array &$call) $default = isset($field[$property]) ? $field[$property] : null; $config = Grav::instance()['config']->get($value, $default); - if (!is_null($config)) { + if (null !== $config) { $field[$property] = $config; } } diff --git a/system/src/Grav/Common/Data/Blueprints.php b/system/src/Grav/Common/Data/Blueprints.php index 51d0c7783..c7090c5ec 100644 --- a/system/src/Grav/Common/Data/Blueprints.php +++ b/system/src/Grav/Common/Data/Blueprints.php @@ -2,7 +2,7 @@ /** * @package Grav.Common.Data * - * @copyright Copyright (C) 2014 - 2017 RocketTheme, LLC. All rights reserved. + * @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -65,11 +65,11 @@ public function types() /** @var \DirectoryIterator $file */ foreach ($iterator as $file) { - if (!$file->isFile() || '.' . $file->getExtension() != YAML_EXT) { + if (!$file->isFile() || '.' . $file->getExtension() !== YAML_EXT) { continue; } $name = $file->getBasename(YAML_EXT); - $this->types[$name] = ucfirst(strtr($name, '_', ' ')); + $this->types[$name] = ucfirst(str_replace('_', ' ', $name)); } } diff --git a/system/src/Grav/Common/Data/Data.php b/system/src/Grav/Common/Data/Data.php index 75eb79d06..a9ceca332 100644 --- a/system/src/Grav/Common/Data/Data.php +++ b/system/src/Grav/Common/Data/Data.php @@ -2,7 +2,7 @@ /** * @package Grav.Common.Data * - * @copyright Copyright (C) 2014 - 2017 RocketTheme, LLC. All rights reserved. + * @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -233,6 +233,7 @@ public function blueprints() /** * Save data if storage has been defined. + * @throws \RuntimeException */ public function save() { diff --git a/system/src/Grav/Common/Data/DataInterface.php b/system/src/Grav/Common/Data/DataInterface.php index bc98982ad..aa61d2604 100644 --- a/system/src/Grav/Common/Data/DataInterface.php +++ b/system/src/Grav/Common/Data/DataInterface.php @@ -2,7 +2,7 @@ /** * @package Grav.Common.Data * - * @copyright Copyright (C) 2014 - 2017 RocketTheme, LLC. All rights reserved. + * @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Data/Validation.php b/system/src/Grav/Common/Data/Validation.php index c6be512b9..8ab5cf185 100644 --- a/system/src/Grav/Common/Data/Validation.php +++ b/system/src/Grav/Common/Data/Validation.php @@ -2,7 +2,7 @@ /** * @package Grav.Common.Data * - * @copyright Copyright (C) 2014 - 2017 RocketTheme, LLC. All rights reserved. + * @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -33,7 +33,7 @@ public static function validate($value, array $field) $method = 'type'.strtr($type, '-', '_'); // If value isn't required, we will stop validation if empty value is given. - if ((empty($validate['required']) || (isset($validate['required']) && $validate['required'] !== true)) && ($value === null || $value === '' || ($field['type'] === 'checkbox' && $value == false))) { + if ((empty($validate['required']) || (isset($validate['required']) && $validate['required'] !== true)) && ($value === null || $value === '' || (($field['type'] === 'checkbox' || $field['type'] === 'switch') && $value == false))) { return $messages; } @@ -41,11 +41,6 @@ public static function validate($value, array $field) $field['type'] = 'text'; } - // If this is a YAML field, stop validation - if (isset($field['yaml']) && $field['yaml'] === true) { - return $messages; - } - // Get language class. $language = Grav::instance()['language']; @@ -54,6 +49,12 @@ public static function validate($value, array $field) ? $language->translate($field['validate']['message']) : $language->translate('FORM.INVALID_INPUT', null, true) . ' "' . $language->translate($name) . '"'; + + // If this is a YAML field validate/filter as such + if ($type != 'yaml' && isset($field['yaml']) && $field['yaml'] === true) { + $method = 'typeYaml'; + } + if (method_exists(__CLASS__, $method)) { $success = self::$method($value, $validate, $field); } else { @@ -100,15 +101,16 @@ public static function filter($value, array $field) $field['type'] = 'text'; } - // If this is a YAML field, simply parse it and return the value. - if (isset($field['yaml']) && $field['yaml'] === true) { - return $value; - } // Validate type with fallback type text. $type = (string) isset($field['validate']['type']) ? $field['validate']['type'] : $field['type']; $method = 'filter' . ucfirst(strtr($type, '-', '_')); + // If this is a YAML field validate/filter as such + if ($type != 'yaml' && isset($field['yaml']) && $field['yaml'] === true) { + $method = 'filterYaml'; + } + if (!method_exists(__CLASS__, $method)) { $method = 'filterText'; } @@ -639,22 +641,16 @@ protected static function filterList($value, array $params, array $field) return (array) $value; } - public static function typeYaml($value, $params) - { - try { - Yaml::parse($value); - return true; - } catch (ParseException $e) { - return false; - } - } - public static function filterYaml($value, $params) { try { - return (array) Yaml::parse($value); + if (is_string($value)) { + return (array) Yaml::parse($value); + } else { + return $value; + } } catch (ParseException $e) { - return null; + return $value; } } diff --git a/system/src/Grav/Common/Data/ValidationException.php b/system/src/Grav/Common/Data/ValidationException.php index 18dd6af68..9272894b0 100644 --- a/system/src/Grav/Common/Data/ValidationException.php +++ b/system/src/Grav/Common/Data/ValidationException.php @@ -2,7 +2,7 @@ /** * @package Grav.Common.Data * - * @copyright Copyright (C) 2014 - 2017 RocketTheme, LLC. All rights reserved. + * @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Debugger.php b/system/src/Grav/Common/Debugger.php index 679772306..4687325e1 100644 --- a/system/src/Grav/Common/Debugger.php +++ b/system/src/Grav/Common/Debugger.php @@ -2,7 +2,7 @@ /** * @package Grav.Common * - * @copyright Copyright (C) 2014 - 2017 RocketTheme, LLC. All rights reserved. + * @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -36,6 +36,9 @@ class Debugger */ public function __construct() { + // Enable debugger until $this->init() gets called. + $this->enabled = true; + $this->debugbar = new StandardDebugBar(); $this->debugbar['time']->addMeasure('Loading', $this->debugbar['time']->getRequestStartTime(), microtime(true)); } @@ -51,6 +54,9 @@ public function init() $this->grav = Grav::instance(); $this->config = $this->grav['config']; + // Enable/disable debugger based on configuration. + $this->enabled = $this->config->get('system.debugger.enabled'); + if ($this->enabled()) { $this->debugbar->addCollector(new ConfigCollector((array)$this->config->get('system'), 'Config')); $this->debugbar->addCollector(new ConfigCollector((array)$this->config->get('plugins'), 'Plugins')); @@ -68,12 +74,8 @@ public function init() */ public function enabled($state = null) { - if (isset($state)) { + if ($state !== null) { $this->enabled = $state; - } else { - if (!isset($this->enabled)) { - $this->enabled = $this->config->get('system.debugger.enabled'); - } } return $this->enabled; @@ -90,8 +92,7 @@ public function addAssets() // Only add assets if Page is HTML $page = $this->grav['page']; - if ($page->templateFormat() != 'html') { - $this->enabled = false; + if ($page->templateFormat() !== 'html') { return $this; } @@ -106,13 +107,13 @@ public function addAssets() // Get the required CSS files list($css_files, $js_files) = $this->renderer->getAssets(null, JavascriptRenderer::RELATIVE_URL); - foreach ($css_files as $css) { + foreach ((array)$css_files as $css) { $assets->addCss($css); } $assets->addCss('/system/assets/debugger.css'); - foreach ($js_files as $js) { + foreach ((array)$js_files as $js) { $assets->addJs($js); } } @@ -163,6 +164,12 @@ public function getCollector($collector) public function render() { if ($this->enabled()) { + // Only add assets if Page is HTML + $page = $this->grav['page']; + if (!$this->renderer || $page->templateFormat() !== 'html') { + return $this; + } + echo $this->renderer->render(); } @@ -176,11 +183,29 @@ public function render() */ public function sendDataInHeaders() { - $this->debugbar->sendDataInHeaders(); + if ($this->enabled()) { + $this->debugbar->sendDataInHeaders(); + } return $this; } + /** + * Returns collected debugger data. + * + * @return array + */ + public function getData() + { + if (!$this->enabled()) { + return null; + } + + $this->timers = []; + + return $this->debugbar->getData(); + } + /** * Start a timer with an associated name and description * @@ -191,7 +216,7 @@ public function sendDataInHeaders() */ public function startTimer($name, $description = null) { - if ($name[0] == '_' || $this->config->get('system.debugger.enabled')) { + if ($name[0] === '_' || $this->enabled()) { $this->debugbar['time']->startMeasure($name, $description); $this->timers[] = $name; } @@ -208,7 +233,7 @@ public function startTimer($name, $description = null) */ public function stopTimer($name) { - if (in_array($name, $this->timers) && ($name[0] == '_' || $this->config->get('system.debugger.enabled'))) { + if (in_array($name, $this->timers, true) && ($name[0] === '_' || $this->enabled())) { $this->debugbar['time']->stopMeasure($name); } diff --git a/system/src/Grav/Common/Errors/BareHandler.php b/system/src/Grav/Common/Errors/BareHandler.php index eb6ac5171..5d8ef9a34 100644 --- a/system/src/Grav/Common/Errors/BareHandler.php +++ b/system/src/Grav/Common/Errors/BareHandler.php @@ -2,7 +2,7 @@ /** * @package Grav.Common.Errors * - * @copyright Copyright (C) 2014 - 2017 RocketTheme, LLC. All rights reserved. + * @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Errors/Errors.php b/system/src/Grav/Common/Errors/Errors.php index 9b7e47c78..c342837d4 100644 --- a/system/src/Grav/Common/Errors/Errors.php +++ b/system/src/Grav/Common/Errors/Errors.php @@ -2,7 +2,7 @@ /** * @package Grav.Common.Errors * - * @copyright Copyright (C) 2014 - 2017 RocketTheme, LLC. All rights reserved. + * @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Errors/SimplePageHandler.php b/system/src/Grav/Common/Errors/SimplePageHandler.php index 9616b9d61..fb2e73f58 100644 --- a/system/src/Grav/Common/Errors/SimplePageHandler.php +++ b/system/src/Grav/Common/Errors/SimplePageHandler.php @@ -2,7 +2,7 @@ /** * @package Grav.Common.Errors * - * @copyright Copyright (C) 2014 - 2017 RocketTheme, LLC. All rights reserved. + * @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -57,6 +57,7 @@ public function handle() * @param $resource * * @return string + * @throws \RuntimeException */ protected function getResource($resource) { @@ -80,8 +81,7 @@ protected function getResource($resource) // If we got this far, nothing was found. throw new \RuntimeException( - "Could not find resource '$resource' in any resource paths." - . "(searched: " . join(", ", $this->searchPaths). ")" + "Could not find resource '{$resource}' in any resource paths (searched: " . implode(', ', $this->searchPaths). ')' ); } @@ -89,7 +89,7 @@ public function addResourcePath($path) { if (!is_dir($path)) { throw new \InvalidArgumentException( - "'$path' is not a valid directory" + "'{$path}' is not a valid directory" ); } diff --git a/system/src/Grav/Common/Errors/SystemFacade.php b/system/src/Grav/Common/Errors/SystemFacade.php index 75aa0e710..5b73a2b77 100644 --- a/system/src/Grav/Common/Errors/SystemFacade.php +++ b/system/src/Grav/Common/Errors/SystemFacade.php @@ -2,7 +2,7 @@ /** * @package Grav.Common.Errors * - * @copyright Copyright (C) 2014 - 2017 RocketTheme, LLC. All rights reserved. + * @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/File/CompiledFile.php b/system/src/Grav/Common/File/CompiledFile.php index 6ff95a813..a0e230f14 100644 --- a/system/src/Grav/Common/File/CompiledFile.php +++ b/system/src/Grav/Common/File/CompiledFile.php @@ -2,7 +2,7 @@ /** * @package Grav.Common.File * - * @copyright Copyright (C) 2014 - 2017 RocketTheme, LLC. All rights reserved. + * @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -42,9 +42,9 @@ public function content($var = null) // Load real file if cache isn't up to date (or is invalid). if ( !isset($cache['@class']) - || $cache['@class'] != $class - || $cache['modified'] != $modified - || $cache['filename'] != $this->filename + || $cache['@class'] !== $class + || $cache['modified'] !== $modified + || $cache['filename'] !== $this->filename ) { // Attempt to lock the file for writing. try { diff --git a/system/src/Grav/Common/File/CompiledJsonFile.php b/system/src/Grav/Common/File/CompiledJsonFile.php index 87b71676e..c2a9a3209 100644 --- a/system/src/Grav/Common/File/CompiledJsonFile.php +++ b/system/src/Grav/Common/File/CompiledJsonFile.php @@ -2,7 +2,7 @@ /** * @package Grav.Common.File * - * @copyright Copyright (C) 2014 - 2017 RocketTheme, LLC. All rights reserved. + * @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/File/CompiledMarkdownFile.php b/system/src/Grav/Common/File/CompiledMarkdownFile.php index 0b038eb48..fa1798f03 100644 --- a/system/src/Grav/Common/File/CompiledMarkdownFile.php +++ b/system/src/Grav/Common/File/CompiledMarkdownFile.php @@ -2,7 +2,7 @@ /** * @package Grav.Common.File * - * @copyright Copyright (C) 2014 - 2017 RocketTheme, LLC. All rights reserved. + * @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/File/CompiledYamlFile.php b/system/src/Grav/Common/File/CompiledYamlFile.php index 2f8276fbc..5861744d0 100644 --- a/system/src/Grav/Common/File/CompiledYamlFile.php +++ b/system/src/Grav/Common/File/CompiledYamlFile.php @@ -2,7 +2,7 @@ /** * @package Grav.Common.File * - * @copyright Copyright (C) 2014 - 2017 RocketTheme, LLC. All rights reserved. + * @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Filesystem/Folder.php b/system/src/Grav/Common/Filesystem/Folder.php index df9dec045..0d691002b 100644 --- a/system/src/Grav/Common/Filesystem/Folder.php +++ b/system/src/Grav/Common/Filesystem/Folder.php @@ -2,7 +2,7 @@ /** * @package Grav.Common.FileSystem * - * @copyright Copyright (C) 2014 - 2017 RocketTheme, LLC. All rights reserved. + * @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -108,8 +108,7 @@ public static function hashAllFiles($path) $files[] = $file->getPathname() . '?'. $file->getMTime(); } - $hash = md5(serialize($files)); - return $hash; + return md5(serialize($files)); } /** @@ -234,7 +233,7 @@ public static function all($path, array $params = []) /** @var \RecursiveDirectoryIterator $file */ foreach ($iterator as $file) { // Ignore hidden files. - if ($file->getFilename()[0] == '.') { + if ($file->getFilename()[0] === '.') { continue; } if (!$folders && $file->isDir()) { @@ -339,7 +338,7 @@ public static function move($source, $target) } // Don't do anything if the source is the same as the new target - if ($source == $target) { + if ($source === $target) { return; } @@ -377,6 +376,7 @@ public static function move($source, $target) * @param string $target * @param bool $include_target * @return bool + * @throws \RuntimeException */ public static function delete($target, $include_target = true) { @@ -435,6 +435,7 @@ public static function create($folder) * @param $dest * * @return bool + * @throws \RuntimeException */ public static function rcopy($src, $dest) { @@ -447,7 +448,7 @@ public static function rcopy($src, $dest) // If the destination directory does not exist create it if (!is_dir($dest)) { - Folder::mkdir($dest); + static::mkdir($dest); } // Open the source directory to read in files @@ -455,10 +456,10 @@ public static function rcopy($src, $dest) /** @var \DirectoryIterator $f */ foreach ($i as $f) { if ($f->isFile()) { - copy($f->getRealPath(), "$dest/" . $f->getFilename()); + copy($f->getRealPath(), "{$dest}/" . $f->getFilename()); } else { if (!$f->isDot() && $f->isDir()) { - static::rcopy($f->getRealPath(), "$dest/$f"); + static::rcopy($f->getRealPath(), "{$dest}/{$f}"); } } } @@ -479,10 +480,10 @@ protected static function doDelete($folder, $include_target = true) } // Go through all items in filesystem and recursively remove everything. - $files = array_diff(scandir($folder), array('.', '..')); + $files = array_diff(scandir($folder, SCANDIR_SORT_NONE), array('.', '..')); foreach ($files as $file) { $path = "{$folder}/{$file}"; - (is_dir($path)) ? self::doDelete($path) : @unlink($path); + is_dir($path) ? self::doDelete($path) : @unlink($path); } return $include_target ? @rmdir($folder) : true; diff --git a/system/src/Grav/Common/Filesystem/RecursiveFolderFilterIterator.php b/system/src/Grav/Common/Filesystem/RecursiveFolderFilterIterator.php index 96e5b7cac..397207422 100644 --- a/system/src/Grav/Common/Filesystem/RecursiveFolderFilterIterator.php +++ b/system/src/Grav/Common/Filesystem/RecursiveFolderFilterIterator.php @@ -2,7 +2,7 @@ /** * @package Grav.Common.FileSystem * - * @copyright Copyright (C) 2014 - 2017 RocketTheme, LLC. All rights reserved. + * @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -37,7 +37,7 @@ public function accept() /** @var $current \SplFileInfo */ $current = $this->current(); - if ($current->isDir() && !in_array($current->getFilename(), $this::$folder_ignores)) { + if ($current->isDir() && !in_array($current->getFilename(), $this::$folder_ignores, true)) { return true; } return false; diff --git a/system/src/Grav/Common/GPM/AbstractCollection.php b/system/src/Grav/Common/GPM/AbstractCollection.php index 2935920fc..3430a6a85 100644 --- a/system/src/Grav/Common/GPM/AbstractCollection.php +++ b/system/src/Grav/Common/GPM/AbstractCollection.php @@ -2,7 +2,7 @@ /** * @package Grav.Common.GPM * - * @copyright Copyright (C) 2014 - 2017 RocketTheme, LLC. All rights reserved. + * @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/GPM/Common/AbstractPackageCollection.php b/system/src/Grav/Common/GPM/Common/AbstractPackageCollection.php index 6d777f33f..78d93bce7 100644 --- a/system/src/Grav/Common/GPM/Common/AbstractPackageCollection.php +++ b/system/src/Grav/Common/GPM/Common/AbstractPackageCollection.php @@ -2,7 +2,7 @@ /** * @package Grav.Common.GPM * - * @copyright Copyright (C) 2014 - 2017 RocketTheme, LLC. All rights reserved. + * @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/GPM/Common/CachedCollection.php b/system/src/Grav/Common/GPM/Common/CachedCollection.php index 31d65219b..0244692e1 100644 --- a/system/src/Grav/Common/GPM/Common/CachedCollection.php +++ b/system/src/Grav/Common/GPM/Common/CachedCollection.php @@ -2,7 +2,7 @@ /** * @package Grav.Common.GPM * - * @copyright Copyright (C) 2014 - 2017 RocketTheme, LLC. All rights reserved. + * @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/GPM/Common/Package.php b/system/src/Grav/Common/GPM/Common/Package.php index 7c6c5bb44..fe4618af7 100644 --- a/system/src/Grav/Common/GPM/Common/Package.php +++ b/system/src/Grav/Common/GPM/Common/Package.php @@ -2,7 +2,7 @@ /** * @package Grav.Common.GPM * - * @copyright Copyright (C) 2014 - 2017 RocketTheme, LLC. All rights reserved. + * @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/GPM/GPM.php b/system/src/Grav/Common/GPM/GPM.php index fd5af4849..a7cbee402 100644 --- a/system/src/Grav/Common/GPM/GPM.php +++ b/system/src/Grav/Common/GPM/GPM.php @@ -2,7 +2,7 @@ /** * @package Grav.Common.GPM * - * @copyright Copyright (C) 2014 - 2017 RocketTheme, LLC. All rights reserved. + * @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -719,8 +719,8 @@ public function getPackagesThatDependOnPackage($slug) foreach ($packages as $package_name => $package) { if (isset($package['dependencies'])) { foreach ($package['dependencies'] as $dependency) { - if (is_array($dependency)) { - $dependency = array_keys($dependency)[0]; + if (is_array($dependency) && isset($dependency['name'])) { + $dependency = $dependency['name']; } if ($dependency == $slug) { @@ -835,6 +835,20 @@ public function getDependencies($packages) continue; } + // Check PHP version + if ($dependency_slug == 'php') { + $current_php_version = phpversion(); + if (version_compare($this->calculateVersionNumberFromDependencyVersion($dependencyVersionWithOperator), + $current_php_version) === 1 + ) { + //Needs a Grav update first + throw new \Exception("One of the packages require PHP " . $dependencies['php'] . ". Please update PHP to resolve this"); + } else { + unset($dependencies[$dependency_slug]); + continue; + } + } + //First, check for Grav dependency. If a dependency requires Grav > the current version, abort and tell. if ($dependency_slug == 'grav') { if (version_compare($this->calculateVersionNumberFromDependencyVersion($dependencyVersionWithOperator), @@ -1062,9 +1076,9 @@ public function calculateVersionNumberFromDependencyVersion($version) } elseif ($version == '') { return null; } elseif ($this->versionFormatIsNextSignificantRelease($version)) { - return substr($version, 1); + return trim(substr($version, 1)); } elseif ($this->versionFormatIsEqualOrHigher($version)) { - return substr($version, 2); + return trim(substr($version, 2)); } else { return $version; } diff --git a/system/src/Grav/Common/GPM/Installer.php b/system/src/Grav/Common/GPM/Installer.php index eadc6501a..275c2fc36 100644 --- a/system/src/Grav/Common/GPM/Installer.php +++ b/system/src/Grav/Common/GPM/Installer.php @@ -2,7 +2,7 @@ /** * @package Grav.Common.GPM * - * @copyright Copyright (C) 2014 - 2017 RocketTheme, LLC. All rights reserved. + * @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/GPM/Licenses.php b/system/src/Grav/Common/GPM/Licenses.php index d5651dbbe..fed5b642e 100644 --- a/system/src/Grav/Common/GPM/Licenses.php +++ b/system/src/Grav/Common/GPM/Licenses.php @@ -2,7 +2,7 @@ /** * @package Grav.Common.GPM * - * @copyright Copyright (C) 2014 - 2017 RocketTheme, LLC. All rights reserved. + * @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/GPM/Local/AbstractPackageCollection.php b/system/src/Grav/Common/GPM/Local/AbstractPackageCollection.php index 038b43bf8..b9762085c 100644 --- a/system/src/Grav/Common/GPM/Local/AbstractPackageCollection.php +++ b/system/src/Grav/Common/GPM/Local/AbstractPackageCollection.php @@ -2,7 +2,7 @@ /** * @package Grav.Common.GPM * - * @copyright Copyright (C) 2014 - 2017 RocketTheme, LLC. All rights reserved. + * @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/GPM/Local/Package.php b/system/src/Grav/Common/GPM/Local/Package.php index 704a8f750..d0fae3a29 100644 --- a/system/src/Grav/Common/GPM/Local/Package.php +++ b/system/src/Grav/Common/GPM/Local/Package.php @@ -2,7 +2,7 @@ /** * @package Grav.Common.GPM * - * @copyright Copyright (C) 2014 - 2017 RocketTheme, LLC. All rights reserved. + * @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/GPM/Local/Packages.php b/system/src/Grav/Common/GPM/Local/Packages.php index 391c68297..3120d21d3 100644 --- a/system/src/Grav/Common/GPM/Local/Packages.php +++ b/system/src/Grav/Common/GPM/Local/Packages.php @@ -2,7 +2,7 @@ /** * @package Grav.Common.GPM * - * @copyright Copyright (C) 2014 - 2017 RocketTheme, LLC. All rights reserved. + * @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/GPM/Local/Plugins.php b/system/src/Grav/Common/GPM/Local/Plugins.php index 34ca76b9e..e97aa5922 100644 --- a/system/src/Grav/Common/GPM/Local/Plugins.php +++ b/system/src/Grav/Common/GPM/Local/Plugins.php @@ -2,7 +2,7 @@ /** * @package Grav.Common.GPM * - * @copyright Copyright (C) 2014 - 2017 RocketTheme, LLC. All rights reserved. + * @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/GPM/Local/Themes.php b/system/src/Grav/Common/GPM/Local/Themes.php index e7f691a88..9b557af9e 100644 --- a/system/src/Grav/Common/GPM/Local/Themes.php +++ b/system/src/Grav/Common/GPM/Local/Themes.php @@ -2,7 +2,7 @@ /** * @package Grav.Common.GPM * - * @copyright Copyright (C) 2014 - 2017 RocketTheme, LLC. All rights reserved. + * @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/GPM/Remote/AbstractPackageCollection.php b/system/src/Grav/Common/GPM/Remote/AbstractPackageCollection.php index 70539cfcd..ffdd90c64 100644 --- a/system/src/Grav/Common/GPM/Remote/AbstractPackageCollection.php +++ b/system/src/Grav/Common/GPM/Remote/AbstractPackageCollection.php @@ -2,7 +2,7 @@ /** * @package Grav.Common.GPM * - * @copyright Copyright (C) 2014 - 2017 RocketTheme, LLC. All rights reserved. + * @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/GPM/Remote/GravCore.php b/system/src/Grav/Common/GPM/Remote/GravCore.php index cccf5683e..ab5287f32 100644 --- a/system/src/Grav/Common/GPM/Remote/GravCore.php +++ b/system/src/Grav/Common/GPM/Remote/GravCore.php @@ -2,7 +2,7 @@ /** * @package Grav.Common.GPM * - * @copyright Copyright (C) 2014 - 2017 RocketTheme, LLC. All rights reserved. + * @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -18,10 +18,12 @@ class GravCore extends AbstractPackageCollection private $version; private $date; + private $min_php; /** * @param bool $refresh * @param null $callback + * @throws \InvalidArgumentException */ public function __construct($refresh = false, $callback = null) { @@ -36,9 +38,10 @@ public function __construct($refresh = false, $callback = null) $this->data = json_decode($this->raw, true); $this->version = isset($this->data['version']) ? $this->data['version'] : '-'; $this->date = isset($this->data['date']) ? $this->data['date'] : '-'; + $this->min_php = isset($this->data['min_php']) ? $this->data['min_php'] : null; if (isset($this->data['assets'])) { - foreach ($this->data['assets'] as $slug => $data) { + foreach ((array)$this->data['assets'] as $slug => $data) { $this->items[$slug] = new Package($data); } } @@ -68,10 +71,10 @@ public function getChangelog($diff = null) } $diffLog = []; - foreach ($this->data['changelog'] as $version => $changelog) { + foreach ((array)$this->data['changelog'] as $version => $changelog) { preg_match("/[\w-\.]+/", $version, $cleanVersion); - if (!$cleanVersion || version_compare($diff, $cleanVersion[0], ">=")) { + if (!$cleanVersion || version_compare($diff, $cleanVersion[0], '>=')) { continue; } @@ -91,6 +94,11 @@ public function getDate() return $this->date; } + /** + * Determine if this version of Grav is eligible to be updated + * + * @return mixed + */ public function isUpdatable() { return version_compare(GRAV_VERSION, $this->getVersion(), '<'); @@ -106,6 +114,25 @@ public function getVersion() return $this->version; } + /** + * Returns the minimum PHP version + * + * @return null|string + */ + public function getMinPHPVersion() + { + // If non min set, assume current PHP version + if (is_null($this->min_php)) { + $this->min_php = phpversion(); + } + return $this->min_php; + } + + /** + * Is this installation symlinked? + * + * @return bool + */ public function isSymlink() { return is_link(GRAV_ROOT . DS . 'index.php'); diff --git a/system/src/Grav/Common/GPM/Remote/Package.php b/system/src/Grav/Common/GPM/Remote/Package.php index ebe51af23..830f63dd5 100644 --- a/system/src/Grav/Common/GPM/Remote/Package.php +++ b/system/src/Grav/Common/GPM/Remote/Package.php @@ -2,7 +2,7 @@ /** * @package Grav.Common.GPM * - * @copyright Copyright (C) 2014 - 2017 RocketTheme, LLC. All rights reserved. + * @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/GPM/Remote/Packages.php b/system/src/Grav/Common/GPM/Remote/Packages.php index ddad08442..d8afd9094 100644 --- a/system/src/Grav/Common/GPM/Remote/Packages.php +++ b/system/src/Grav/Common/GPM/Remote/Packages.php @@ -2,7 +2,7 @@ /** * @package Grav.Common.GPM * - * @copyright Copyright (C) 2014 - 2017 RocketTheme, LLC. All rights reserved. + * @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/GPM/Remote/Plugins.php b/system/src/Grav/Common/GPM/Remote/Plugins.php index 10e273278..74e5ffb09 100644 --- a/system/src/Grav/Common/GPM/Remote/Plugins.php +++ b/system/src/Grav/Common/GPM/Remote/Plugins.php @@ -2,7 +2,7 @@ /** * @package Grav.Common.GPM * - * @copyright Copyright (C) 2014 - 2017 RocketTheme, LLC. All rights reserved. + * @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/GPM/Remote/Themes.php b/system/src/Grav/Common/GPM/Remote/Themes.php index 07ffbdcf5..3f3f96cb0 100644 --- a/system/src/Grav/Common/GPM/Remote/Themes.php +++ b/system/src/Grav/Common/GPM/Remote/Themes.php @@ -2,7 +2,7 @@ /** * @package Grav.Common.GPM * - * @copyright Copyright (C) 2014 - 2017 RocketTheme, LLC. All rights reserved. + * @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/GPM/Response.php b/system/src/Grav/Common/GPM/Response.php index ffb8caf05..d62acd23b 100644 --- a/system/src/Grav/Common/GPM/Response.php +++ b/system/src/Grav/Common/GPM/Response.php @@ -2,7 +2,7 @@ /** * @package Grav.Common.GPM * - * @copyright Copyright (C) 2014 - 2017 RocketTheme, LLC. All rights reserved. + * @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/GPM/Upgrader.php b/system/src/Grav/Common/GPM/Upgrader.php index 72390095b..1c995bc4c 100644 --- a/system/src/Grav/Common/GPM/Upgrader.php +++ b/system/src/Grav/Common/GPM/Upgrader.php @@ -2,7 +2,7 @@ /** * @package Grav.Common.GPM * - * @copyright Copyright (C) 2014 - 2017 RocketTheme, LLC. All rights reserved. + * @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -24,11 +24,14 @@ class Upgrader */ private $remote; + private $min_php; + /** * Creates a new GPM instance with Local and Remote packages available * * @param boolean $refresh Applies to Remote Packages only and forces a refetch of data * @param callable $callback Either a function or callback in array notation + * @throws \InvalidArgumentException */ public function __construct($refresh = false, $callback = null) { @@ -88,17 +91,33 @@ public function getChangelog($diff = null) } /** + * Make sure this meets minimum PHP requirements + * * @return bool */ public function meetsRequirements() { - if (version_compare(PHP_VERSION, GRAV_PHP_MIN, '<')) { + $current_php_version = phpversion(); + if (version_compare($current_php_version, $this->minPHPVersion(), '<')) { return false; } return true; } + /** + * Get minimum PHP version from remote + * + * @return null + */ + public function minPHPVersion() + { + if (is_null($this->min_php)) { + $this->min_php = $this->remote->getMinPHPVersion(); + } + return $this->min_php; + } + /** * Checks if the currently installed Grav is upgradable to a newer version * diff --git a/system/src/Grav/Common/Getters.php b/system/src/Grav/Common/Getters.php index 659ba15a7..6f58fba20 100644 --- a/system/src/Grav/Common/Getters.php +++ b/system/src/Grav/Common/Getters.php @@ -2,7 +2,7 @@ /** * @package Grav.Common * - * @copyright Copyright (C) 2014 - 2017 RocketTheme, LLC. All rights reserved. + * @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Grav.php b/system/src/Grav/Common/Grav.php index 99566c897..e6a49a165 100644 --- a/system/src/Grav/Common/Grav.php +++ b/system/src/Grav/Common/Grav.php @@ -2,7 +2,7 @@ /** * @package Grav.Common * - * @copyright Copyright (C) 2014 - 2017 RocketTheme, LLC. All rights reserved. + * @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -15,6 +15,7 @@ use Grav\Common\Page\Page; use RocketTheme\Toolbox\DI\Container; use RocketTheme\Toolbox\Event\Event; +use RocketTheme\Toolbox\Event\EventDispatcher; class Grav extends Container { @@ -38,8 +39,7 @@ class Grav extends Container 'uri' => 'Grav\Common\Uri', 'events' => 'RocketTheme\Toolbox\Event\EventDispatcher', 'cache' => 'Grav\Common\Cache', - 'session' => 'Grav\Common\Session', - 'Grav\Common\Service\MessagesServiceProvider', + 'Grav\Common\Service\SessionServiceProvider', 'plugins' => 'Grav\Common\Plugins', 'themes' => 'Grav\Common\Themes', 'twig' => 'Grav\Common\Twig\Twig', @@ -256,11 +256,6 @@ public function header() header('ETag: "' . md5($page->raw() . $page->modified()).'"'); } - // Set debugger data in headers - if (!($format === null || $format == 'html')) { - $this['debugger']->enabled(false); - } - // Set HTTP response code if (isset($this['page']->header()->http_response_code)) { http_response_code($this['page']->header()->http_response_code); @@ -440,7 +435,7 @@ protected function registerService($serviceKey, $serviceClass) */ public function fallbackUrl($path) { - $this->fireEvent('onPageFallBackUrl'); + $this->fireEvent('onPageFallBackUrl'); /** @var Uri $uri */ $uri = $this['uri']; @@ -453,10 +448,11 @@ public function fallbackUrl($path) $supported_types = $config->get('media.types'); // Check whitelist first, then ensure extension is a valid media type - if (!empty($fallback_types) && !in_array($uri_extension, $fallback_types)) { - return; - } elseif (!array_key_exists($uri_extension, $supported_types)) { - return; + if (!empty($fallback_types) && !\in_array($uri_extension, $fallback_types, true)) { + return false; + } + if (!array_key_exists($uri_extension, $supported_types)) { + return false; } $path_parts = pathinfo($path); diff --git a/system/src/Grav/Common/GravTrait.php b/system/src/Grav/Common/GravTrait.php index 36692d6cc..7ff04e65e 100644 --- a/system/src/Grav/Common/GravTrait.php +++ b/system/src/Grav/Common/GravTrait.php @@ -2,12 +2,15 @@ /** * @package Grav.Common * - * @copyright Copyright (C) 2014 - 2017 RocketTheme, LLC. All rights reserved. + * @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ namespace Grav\Common; +/** + * @deprecated 2.0 + */ trait GravTrait { protected static $grav; @@ -21,7 +24,9 @@ public static function getGrav() self::$grav = Grav::instance(); } + $caller = self::$grav['debugger']->getCaller(); + self::$grav['debugger']->addMessage("Deprecated GravTrait used in {$caller['file']}", 'deprecated'); + return self::$grav; } } - diff --git a/system/src/Grav/Common/Helpers/Base32.php b/system/src/Grav/Common/Helpers/Base32.php index 33cb0f194..f212e5856 100644 --- a/system/src/Grav/Common/Helpers/Base32.php +++ b/system/src/Grav/Common/Helpers/Base32.php @@ -2,7 +2,7 @@ /** * @package Grav.Common.Helpers * - * @copyright Copyright (C) 2014 - 2017 RocketTheme, LLC. All rights reserved. + * @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -32,7 +32,7 @@ class Base32 { */ public static function encode( $bytes ) { $i = 0; $index = 0; $digit = 0; - $base32 = ""; + $base32 = ''; $bytes_len = strlen($bytes); while( $i < $bytes_len ) { $currByte = ord($bytes{$i}); @@ -51,7 +51,7 @@ public static function encode( $bytes ) { } else { $digit = ($currByte >> (8 - ($index + 5))) & 0x1F; $index = ($index + 5) % 8; - if( $index == 0 ) $i++; + if( $index === 0 ) $i++; } $base32 .= self::$base32Chars{$digit}; } @@ -96,7 +96,7 @@ public static function decode( $base32 ) { $bytes[$offset] |= $digit << (8 - $index); } } - $bites = ""; + $bites = ''; foreach( $bytes as $byte ) $bites .= chr($byte); return $bites; } diff --git a/system/src/Grav/Common/Helpers/Excerpts.php b/system/src/Grav/Common/Helpers/Excerpts.php index 15edec749..d774bcf46 100644 --- a/system/src/Grav/Common/Helpers/Excerpts.php +++ b/system/src/Grav/Common/Helpers/Excerpts.php @@ -2,7 +2,7 @@ /** * @package Grav.Common.Helpers * - * @copyright Copyright (C) 2014 - 2017 RocketTheme, LLC. All rights reserved. + * @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -56,7 +56,7 @@ public static function processImageHtml($html, Page $page) public static function getExcerptFromHtml($html, $tag) { $doc = new \DOMDocument(); - $doc->loadHtml($html); + $doc->loadHTML($html); $images = $doc->getElementsByTagName($tag); $excerpt = null; @@ -142,9 +142,9 @@ public static function processLinkExcerpt($excerpt, Page $page, $type = 'link') foreach ($actions as $attrib => $value) { $key = $attrib; - if (in_array($attrib, $valid_attributes)) { + if (in_array($attrib, $valid_attributes, true)) { // support both class and classes. - if ($attrib == 'classes') { + if ($attrib === 'classes') { $attrib = 'class'; } $excerpt['element']['attributes'][$attrib] = str_replace(',', ' ', $value); @@ -211,8 +211,8 @@ public static function processImageExcerpt(array $excerpt, Page $page) } else { // File is also local if scheme is http(s) and host matches. $local_file = isset($url_parts['path']) - && (empty($url_parts['scheme']) || in_array($url_parts['scheme'], ['http', 'https'])) - && (empty($url_parts['host']) || $url_parts['host'] == Grav::instance()['uri']->host()); + && (empty($url_parts['scheme']) || in_array($url_parts['scheme'], ['http', 'https'], true)) + && (empty($url_parts['host']) || $url_parts['host'] === Grav::instance()['uri']->host()); if ($local_file) { $filename = basename($url_parts['path']); @@ -246,13 +246,14 @@ public static function processImageExcerpt(array $excerpt, Page $page) // Process operations $medium = static::processMediaActions($medium, $url_parts); + $element_excerpt = $excerpt['element']['attributes']; - $alt = isset($excerpt['element']['attributes']['alt']) ? $excerpt['element']['attributes']['alt'] : ''; - $title = isset($excerpt['element']['attributes']['title']) ? $excerpt['element']['attributes']['title'] : ''; - $class = isset($excerpt['element']['attributes']['class']) ? $excerpt['element']['attributes']['class'] : ''; - $id = isset($excerpt['element']['attributes']['id']) ? $excerpt['element']['attributes']['id'] : ''; + $alt = isset($element_excerpt['alt']) ? $element_excerpt['alt'] : ''; + $title = isset($element_excerpt['title']) ? $element_excerpt['title'] : ''; + $class = isset($element_excerpt['class']) ? $element_excerpt['class'] : ''; + $id = isset($element_excerpt['id']) ? $element_excerpt['id'] : ''; - $excerpt['element'] = $medium->parseDownElement($title, $alt, $class, $id, true); + $excerpt['element'] = $medium->parsedownElement($title, $alt, $class, $id, true); } else { // Not a current page media file, see if it needs converting to relative. diff --git a/system/src/Grav/Common/Helpers/Exif.php b/system/src/Grav/Common/Helpers/Exif.php index e33cc7366..ee0c4cae7 100644 --- a/system/src/Grav/Common/Helpers/Exif.php +++ b/system/src/Grav/Common/Helpers/Exif.php @@ -2,25 +2,30 @@ /** * @package Grav.Common.Helpers * - * @copyright Copyright (C) 2014 - 2017 RocketTheme, LLC. All rights reserved. + * @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ namespace Grav\Common\Helpers; use Grav\Common\Grav; +use SebastianBergmann\GlobalState\RuntimeException; class Exif { public $reader; + /** + * Exif constructor. + * @throws RuntimeException + */ public function __construct() { if (Grav::instance()['config']->get('system.media.auto_metadata_exif')) { if (function_exists('exif_read_data') && class_exists('\PHPExif\Reader\Reader')) { $this->reader = \PHPExif\Reader\Reader::factory(\PHPExif\Reader\Reader::TYPE_NATIVE); } else { - throw new \Exception('Please enable the Exif extension for PHP or disable Exif support in Grav system configuration'); + throw new \RuntimeException('Please enable the Exif extension for PHP or disable Exif support in Grav system configuration'); } } } diff --git a/system/src/Grav/Common/Helpers/Truncator.php b/system/src/Grav/Common/Helpers/Truncator.php index 1318b7e66..c2e734989 100644 --- a/system/src/Grav/Common/Helpers/Truncator.php +++ b/system/src/Grav/Common/Helpers/Truncator.php @@ -2,7 +2,7 @@ /** * @package Grav.Common.Helpers * - * @copyright Copyright (C) 2014 - 2017 RocketTheme, LLC. All rights reserved. + * @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -34,7 +34,7 @@ class Truncator { * @param string $ellipsis String to use as ellipsis (if any). * @return string Safe truncated HTML. */ - public static function truncateWords($html, $limit = 0, $ellipsis = "") + public static function truncateWords($html, $limit = 0, $ellipsis = '') { if ($limit <= 0) { return $html; @@ -94,7 +94,7 @@ public static function truncateLetters($html, $limit = 0, $ellipsis = "") $dom = self::htmlToDomDocument($html); // Grab the body of our DOM. - $body = $dom->getElementsByTagName("body")->item(0); + $body = $dom->getElementsByTagName('body')->item(0); // Iterate over letters. $letters = new DOMLettersIterator($body); @@ -181,7 +181,7 @@ private static function insertEllipsis($domNode, $ellipsis) { $avoid = array('a', 'strong', 'em', 'h1', 'h2', 'h3', 'h4', 'h5'); //html tags to avoid appending the ellipsis to - if (in_array($domNode->parentNode->nodeName, $avoid) && $domNode->parentNode->parentNode !== null) { + if ($domNode->parentNode->parentNode !== null && in_array($domNode->parentNode->nodeName, $avoid, true)) { // Append as text node to parent instead $textNode = new DOMText($ellipsis); @@ -204,7 +204,7 @@ private static function insertEllipsis($domNode, $ellipsis) * @return string */ private static function innerHTML($element) { - $innerHTML = ""; + $innerHTML = ''; $children = $element->childNodes; foreach ($children as $child) { diff --git a/system/src/Grav/Common/Inflector.php b/system/src/Grav/Common/Inflector.php index d3138ccb6..f4a17ba7c 100644 --- a/system/src/Grav/Common/Inflector.php +++ b/system/src/Grav/Common/Inflector.php @@ -2,7 +2,7 @@ /** * @package Grav.Common * - * @copyright Copyright (C) 2014 - 2017 RocketTheme, LLC. All rights reserved. + * @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Iterator.php b/system/src/Grav/Common/Iterator.php index 0924e6449..99fc0c48e 100644 --- a/system/src/Grav/Common/Iterator.php +++ b/system/src/Grav/Common/Iterator.php @@ -2,7 +2,7 @@ /** * @package Grav.Common * - * @copyright Copyright (C) 2014 - 2017 RocketTheme, LLC. All rights reserved. + * @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Language/Language.php b/system/src/Grav/Common/Language/Language.php index ae9546a22..173a4a7d6 100644 --- a/system/src/Grav/Common/Language/Language.php +++ b/system/src/Grav/Common/Language/Language.php @@ -2,7 +2,7 @@ /** * @package Grav.Common.Language * - * @copyright Copyright (C) 2014 - 2017 RocketTheme, LLC. All rights reserved. + * @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -174,23 +174,23 @@ public function setActiveFromUri($uri) // if languages set if ($this->enabled()) { - // try setting from prefix of URL (/en/blah/blah) + // Try setting language from prefix of URL (/en/blah/blah). if (preg_match($regex, $uri, $matches)) { $this->lang_in_url = true; $this->active = $matches[2]; - $uri = preg_replace("/\\" . $matches[1] . "/", '', $uri, 1); + $uri = preg_replace("/\\" . $matches[1] . '/', '', $uri, 1); - // store in session if different - if ($this->config->get('system.session.enabled', false) + // Store in session if language is different. + if (isset($this->grav['session']) && $this->grav['session']->started() && $this->config->get('system.languages.session_store_active', true) && $this->grav['session']->active_language != $this->active ) { $this->grav['session']->active_language = $this->active; } } else { - // try getting from session, else no active - if ($this->config->get('system.session.enabled', false) && - $this->config->get('system.languages.session_store_active', true)) { + // Try getting language from the session, else no active. + if (isset($this->grav['session']) && $this->grav['session']->started() + && $this->config->get('system.languages.session_store_active', true)) { $this->active = $this->grav['session']->active_language ?: null; } // if still null, try from http_accept_language header @@ -203,8 +203,8 @@ public function setActiveFromUri($uri) } } - // repeat if not found, try base language only - fixes Safari sending the language code always - // with a locale (e.g. it-it or fr-fr) + // Repeat if not found, try base language only - fixes Safari sending the language code always + // with a locale (e.g. it-it or fr-fr). foreach ($preferred as $lang) { $lang = substr($lang, 0, 2); if ($this->validate($lang)) { diff --git a/system/src/Grav/Common/Language/LanguageCodes.php b/system/src/Grav/Common/Language/LanguageCodes.php index 68225c8e4..8be5b5b04 100644 --- a/system/src/Grav/Common/Language/LanguageCodes.php +++ b/system/src/Grav/Common/Language/LanguageCodes.php @@ -2,7 +2,7 @@ /** * @package Grav.Common.Language * - * @copyright Copyright (C) 2014 - 2017 RocketTheme, LLC. All rights reserved. + * @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -157,14 +157,13 @@ public static function getNativeName($code) { if (isset(static::$codes[$code])) { return static::get($code, 'nativeName'); - } else { - if (preg_match('/[a-zA-Z]{2}-[a-zA-Z]{2}/', $code)) { - return static::get(substr($code, 0, 2), 'nativeName') . ' (' . substr($code, -2) . ')'; - } else { - return $code; - } + } + if (preg_match('/[a-zA-Z]{2}-[a-zA-Z]{2}/', $code)) { + return static::get(substr($code, 0, 2), 'nativeName') . ' (' . substr($code, -2) . ')'; } + + return $code; } public static function getOrientation($code) @@ -179,7 +178,7 @@ public static function getOrientation($code) public static function isRtl($code) { - if (static::getOrientation($code) == 'rtl') { + if (static::getOrientation($code) === 'rtl') { return true; } return false; @@ -192,7 +191,6 @@ public static function getNames(array $keys) if (isset(static::$codes[$key])) { $results[$key] = static::$codes[$key]; } - } return $results; } @@ -201,8 +199,8 @@ protected static function get($code, $type) { if (isset(static::$codes[$code][$type])) { return static::$codes[$code][$type]; - } else { - return false; } + + return false; } } diff --git a/system/src/Grav/Common/Markdown/Parsedown.php b/system/src/Grav/Common/Markdown/Parsedown.php index c9ec34d8a..b066ad3bc 100644 --- a/system/src/Grav/Common/Markdown/Parsedown.php +++ b/system/src/Grav/Common/Markdown/Parsedown.php @@ -2,7 +2,7 @@ /** * @package Grav.Common.Markdown * - * @copyright Copyright (C) 2014 - 2017 RocketTheme, LLC. All rights reserved. + * @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Markdown/ParsedownExtra.php b/system/src/Grav/Common/Markdown/ParsedownExtra.php index a96dfca59..481c52f1d 100644 --- a/system/src/Grav/Common/Markdown/ParsedownExtra.php +++ b/system/src/Grav/Common/Markdown/ParsedownExtra.php @@ -2,7 +2,7 @@ /** * @package Grav.Common.Markdown * - * @copyright Copyright (C) 2014 - 2017 RocketTheme, LLC. All rights reserved. + * @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -17,10 +17,12 @@ class ParsedownExtra extends \ParsedownExtra * * @param $page * @param $defaults + * @throws \Exception */ public function __construct($page, $defaults) { parent::__construct(); + $this->init($page, $defaults); } } diff --git a/system/src/Grav/Common/Markdown/ParsedownGravTrait.php b/system/src/Grav/Common/Markdown/ParsedownGravTrait.php index c8266eb7b..59be69b0e 100644 --- a/system/src/Grav/Common/Markdown/ParsedownGravTrait.php +++ b/system/src/Grav/Common/Markdown/ParsedownGravTrait.php @@ -2,7 +2,7 @@ /** * @package Grav.Common.Markdown * - * @copyright Copyright (C) 2014 - 2017 RocketTheme, LLC. All rights reserved. + * @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -35,7 +35,7 @@ protected function init($page, $defaults) $grav = Grav::instance(); $this->page = $page; - $this->BlockTypes['{'] [] = "TwigTag"; + $this->BlockTypes['{'] [] = 'TwigTag'; $this->special_chars = ['>' => 'gt', '<' => 'lt', '"' => 'quot']; if ($defaults === null) { @@ -56,6 +56,9 @@ protected function init($page, $defaults) * * @param $type * @param $tag + * @param bool $continuable + * @param bool $completable + * @param $index */ public function addBlockType($type, $tag, $continuable = false, $completable = false, $index = null) { @@ -67,7 +70,7 @@ public function addBlockType($type, $tag, $continuable = false, $completable = f $block = &$this->BlockTypes[$type]; } - if (!isset($index)) { + if (null === $index) { $block[] = $tag; } else { array_splice($block, $index, 0, [$tag]); @@ -86,10 +89,11 @@ public function addBlockType($type, $tag, $continuable = false, $completable = f * * @param $type * @param $tag + * @param $index */ public function addInlineType($type, $tag, $index = null) { - if (!isset($index) || !isset($this->InlineTypes[$type])) { + if (null === $index || !isset($this->InlineTypes[$type])) { $this->InlineTypes[$type] [] = $tag; } else { array_splice($this->InlineTypes[$type], $index, 0, [$tag]); @@ -109,7 +113,7 @@ public function addInlineType($type, $tag, $index = null) */ protected function isBlockContinuable($Type) { - $continuable = in_array($Type, $this->continuable_blocks) || method_exists($this, 'block' . $Type . 'Continue'); + $continuable = \in_array($Type, $this->continuable_blocks) || method_exists($this, 'block' . $Type . 'Continue'); return $continuable; } @@ -123,7 +127,7 @@ protected function isBlockContinuable($Type) */ protected function isBlockCompletable($Type) { - $completable = in_array($Type, $this->completable_blocks) || method_exists($this, 'block' . $Type . 'Complete'); + $completable = \in_array($Type, $this->completable_blocks) || method_exists($this, 'block' . $Type . 'Complete'); return $completable; } @@ -157,32 +161,31 @@ public function setSpecialChars($special_chars) /** * Ensure Twig tags are treated as block level items with no

tags + * + * @param array $line + * @return array|null */ - protected function blockTwigTag($Line) + protected function blockTwigTag($line) { - if (preg_match('/(?:{{|{%|{#)(.*)(?:}}|%}|#})/', $Line['body'], $matches)) { - $Block = [ - 'markup' => $Line['body'], - ]; - - return $Block; + if (preg_match('/(?:{{|{%|{#)(.*)(?:}}|%}|#})/', $line['body'], $matches)) { + return ['markup' => $line['body']]; } return null; } - protected function inlineSpecialCharacter($Excerpt) + protected function inlineSpecialCharacter($excerpt) { - if ($Excerpt['text'][0] === '&' && !preg_match('/^&#?\w+;/', $Excerpt['text'])) { + if ($excerpt['text'][0] === '&' && !preg_match('/^&#?\w+;/', $excerpt['text'])) { return [ 'markup' => '&', 'extent' => 1, ]; } - if (isset($this->special_chars[$Excerpt['text'][0]])) { + if (isset($this->special_chars[$excerpt['text'][0]])) { return [ - 'markup' => '&' . $this->special_chars[$Excerpt['text'][0]] . ';', + 'markup' => '&' . $this->special_chars[$excerpt['text'][0]] . ';', 'extent' => 1, ]; } @@ -199,11 +202,11 @@ protected function inlineImage($excerpt) $excerpt['extent'] = $excerpt['extent'] + strlen($matches[1]) - 1; return $excerpt; - } else { - $excerpt['type'] = 'image'; - $excerpt = parent::inlineImage($excerpt); } + $excerpt['type'] = 'image'; + $excerpt = parent::inlineImage($excerpt); + // if this is an image process it if (isset($excerpt['element']['attributes']['src'])) { $excerpt = Excerpts::processImageExcerpt($excerpt, $this->page); @@ -228,10 +231,10 @@ protected function inlineLink($excerpt) $excerpt['extent'] = $excerpt['extent'] + strlen($matches[1]) - 1; return $excerpt; - } else { - $excerpt = parent::inlineLink($excerpt); } + $excerpt = parent::inlineLink($excerpt); + // if this is a link if (isset($excerpt['element']['attributes']['href'])) { $excerpt = Excerpts::processLinkExcerpt($excerpt, $this->page, $type); @@ -243,10 +246,10 @@ protected function inlineLink($excerpt) // For extending this class via plugins public function __call($method, $args) { - if (isset($this->$method) === true) { - $func = $this->$method; + if (isset($this->{$method}) === true) { + $func = $this->{$method}; - return call_user_func_array($func, $args); + return \call_user_func_array($func, $args); } return null; diff --git a/system/src/Grav/Common/Page/Collection.php b/system/src/Grav/Common/Page/Collection.php index 386b93134..5113e811f 100644 --- a/system/src/Grav/Common/Page/Collection.php +++ b/system/src/Grav/Common/Page/Collection.php @@ -2,7 +2,7 @@ /** * @package Grav.Common.Page * - * @copyright Copyright (C) 2014 - 2017 RocketTheme, LLC. All rights reserved. + * @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -610,4 +610,23 @@ public function ofOneOfTheseAccessLevels($accessLevels) return $this; } + + /** + * Get the extended version of this Collection with each page keyed by route + * + * @return array + * @throws \Exception + */ + public function toExtendedArray() + { + $items = []; + foreach ($this->items as $path => $slug) { + $page = $this->pages->get($path); + + if ($page !== null) { + $items[$page->route()] = $page->toArray(); + } + } + return $items; + } } diff --git a/system/src/Grav/Common/Page/Header.php b/system/src/Grav/Common/Page/Header.php index 9003cec62..7df862bb1 100644 --- a/system/src/Grav/Common/Page/Header.php +++ b/system/src/Grav/Common/Page/Header.php @@ -2,7 +2,7 @@ /** * @package Grav.Common.Page * - * @copyright Copyright (C) 2014 - 2017 RocketTheme, LLC. All rights reserved. + * @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Page/Media.php b/system/src/Grav/Common/Page/Media.php index df6290e4b..bd97cb2e2 100644 --- a/system/src/Grav/Common/Page/Media.php +++ b/system/src/Grav/Common/Page/Media.php @@ -2,7 +2,7 @@ /** * @package Grav.Common.Page * - * @copyright Copyright (C) 2014 - 2017 RocketTheme, LLC. All rights reserved. + * @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Page/Medium/AbstractMedia.php b/system/src/Grav/Common/Page/Medium/AbstractMedia.php index cc428b992..fe0ac62ed 100644 --- a/system/src/Grav/Common/Page/Medium/AbstractMedia.php +++ b/system/src/Grav/Common/Page/Medium/AbstractMedia.php @@ -2,7 +2,7 @@ /** * @package Grav.Common.Page * - * @copyright Copyright (C) 2014 - 2017 RocketTheme, LLC. All rights reserved. + * @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Page/Medium/AudioMedium.php b/system/src/Grav/Common/Page/Medium/AudioMedium.php index 1a2821d8e..aae359776 100644 --- a/system/src/Grav/Common/Page/Medium/AudioMedium.php +++ b/system/src/Grav/Common/Page/Medium/AudioMedium.php @@ -2,7 +2,7 @@ /** * @package Grav.Common.Page * - * @copyright Copyright (C) 2014 - 2017 RocketTheme, LLC. All rights reserved. + * @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Page/Medium/GlobalMedia.php b/system/src/Grav/Common/Page/Medium/GlobalMedia.php index 47360d32e..74adc088d 100644 --- a/system/src/Grav/Common/Page/Medium/GlobalMedia.php +++ b/system/src/Grav/Common/Page/Medium/GlobalMedia.php @@ -2,7 +2,7 @@ /** * @package Grav.Common.Page * - * @copyright Copyright (C) 2014 - 2017 RocketTheme, LLC. All rights reserved. + * @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Page/Medium/ImageFile.php b/system/src/Grav/Common/Page/Medium/ImageFile.php index 57e6b847e..683448872 100644 --- a/system/src/Grav/Common/Page/Medium/ImageFile.php +++ b/system/src/Grav/Common/Page/Medium/ImageFile.php @@ -2,7 +2,7 @@ /** * @package Grav.Common.Page * - * @copyright Copyright (C) 2014 - 2017 RocketTheme, LLC. All rights reserved. + * @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Page/Medium/ImageMedium.php b/system/src/Grav/Common/Page/Medium/ImageMedium.php index 23b5b3ce7..05bc4c486 100644 --- a/system/src/Grav/Common/Page/Medium/ImageMedium.php +++ b/system/src/Grav/Common/Page/Medium/ImageMedium.php @@ -2,7 +2,7 @@ /** * @package Grav.Common.Page * - * @copyright Copyright (C) 2014 - 2017 RocketTheme, LLC. All rights reserved. + * @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Page/Medium/Link.php b/system/src/Grav/Common/Page/Medium/Link.php index acea8fefc..15aac7b3b 100644 --- a/system/src/Grav/Common/Page/Medium/Link.php +++ b/system/src/Grav/Common/Page/Medium/Link.php @@ -2,7 +2,7 @@ /** * @package Grav.Common.Page * - * @copyright Copyright (C) 2014 - 2017 RocketTheme, LLC. All rights reserved. + * @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Page/Medium/Medium.php b/system/src/Grav/Common/Page/Medium/Medium.php index 0d571c10d..4811c8d54 100644 --- a/system/src/Grav/Common/Page/Medium/Medium.php +++ b/system/src/Grav/Common/Page/Medium/Medium.php @@ -2,7 +2,7 @@ /** * @package Grav.Common.Page * - * @copyright Copyright (C) 2014 - 2017 RocketTheme, LLC. All rights reserved. + * @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -92,6 +92,20 @@ public function meta() return new Data($this->items); } + /** + * Check if this medium exists or not + * + * @return bool + */ + public function exists() + { + $path = $this->get('filepath'); + if (file_exists($path)) { + return true; + } + return false; + } + /** * Returns an array containing just the metadata * @@ -270,10 +284,14 @@ public function parsedownElement($title = null, $alt = null, $class = null, $id } if (empty($attributes['alt'])) { - if (!empty($alt) || $alt === '') { + if (!empty($alt)) { $attributes['alt'] = $alt; } elseif (!empty($this->items['alt'])) { $attributes['alt'] = $this->items['alt']; + } elseif (!empty($this->items['alt_text'])) { + $attributes['alt'] = $this->items['alt_text']; + } else { + $attributes['alt'] = ''; } } diff --git a/system/src/Grav/Common/Page/Medium/MediumFactory.php b/system/src/Grav/Common/Page/Medium/MediumFactory.php index 9c6a0a536..b5b197e5b 100644 --- a/system/src/Grav/Common/Page/Medium/MediumFactory.php +++ b/system/src/Grav/Common/Page/Medium/MediumFactory.php @@ -2,7 +2,7 @@ /** * @package Grav.Common.Page * - * @copyright Copyright (C) 2014 - 2017 RocketTheme, LLC. All rights reserved. + * @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -34,7 +34,7 @@ public static function fromFile($file, array $params = []) $config = Grav::instance()['config']; - $media_params = $config->get("media.types.".strtolower($ext)); + $media_params = $config->get("media.types." . strtolower($ext)); if (!$media_params) { return null; } @@ -106,7 +106,7 @@ public static function fromArray(array $items = [], Blueprint $blueprint = null) * @param ImageMedium $medium * @param int $from * @param int $to - * @return Medium + * @return Medium|array */ public static function scaledFromMedium($medium, $from, $to) { diff --git a/system/src/Grav/Common/Page/Medium/ParsedownHtmlTrait.php b/system/src/Grav/Common/Page/Medium/ParsedownHtmlTrait.php index 48619d186..aaf6fdeef 100644 --- a/system/src/Grav/Common/Page/Medium/ParsedownHtmlTrait.php +++ b/system/src/Grav/Common/Page/Medium/ParsedownHtmlTrait.php @@ -2,7 +2,7 @@ /** * @package Grav.Common.Page * - * @copyright Copyright (C) 2014 - 2017 RocketTheme, LLC. All rights reserved. + * @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Page/Medium/RenderableInterface.php b/system/src/Grav/Common/Page/Medium/RenderableInterface.php index 61650deb2..35b03782c 100644 --- a/system/src/Grav/Common/Page/Medium/RenderableInterface.php +++ b/system/src/Grav/Common/Page/Medium/RenderableInterface.php @@ -2,7 +2,7 @@ /** * @package Grav.Common.Page * - * @copyright Copyright (C) 2014 - 2017 RocketTheme, LLC. All rights reserved. + * @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Page/Medium/StaticImageMedium.php b/system/src/Grav/Common/Page/Medium/StaticImageMedium.php index 0dc8337ce..047e7ed22 100644 --- a/system/src/Grav/Common/Page/Medium/StaticImageMedium.php +++ b/system/src/Grav/Common/Page/Medium/StaticImageMedium.php @@ -2,7 +2,7 @@ /** * @package Grav.Common.Page * - * @copyright Copyright (C) 2014 - 2017 RocketTheme, LLC. All rights reserved. + * @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Page/Medium/StaticResizeTrait.php b/system/src/Grav/Common/Page/Medium/StaticResizeTrait.php index 42d87879f..4e7d61901 100644 --- a/system/src/Grav/Common/Page/Medium/StaticResizeTrait.php +++ b/system/src/Grav/Common/Page/Medium/StaticResizeTrait.php @@ -2,7 +2,7 @@ /** * @package Grav.Common.Page * - * @copyright Copyright (C) 2014 - 2017 RocketTheme, LLC. All rights reserved. + * @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Page/Medium/ThumbnailImageMedium.php b/system/src/Grav/Common/Page/Medium/ThumbnailImageMedium.php index 82c9501c8..6f88a2cce 100644 --- a/system/src/Grav/Common/Page/Medium/ThumbnailImageMedium.php +++ b/system/src/Grav/Common/Page/Medium/ThumbnailImageMedium.php @@ -2,7 +2,7 @@ /** * @package Grav.Common.Page * - * @copyright Copyright (C) 2014 - 2017 RocketTheme, LLC. All rights reserved. + * @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -123,8 +123,8 @@ protected function bubble($method, array $arguments = [], $testLinked = true) { if (!$testLinked || $this->linked) { return $this->parent ? call_user_func_array(array($this->parent, $method), $arguments) : $this; - } else { - return call_user_func_array(array($this, 'parent::' . $method), $arguments); } + + return call_user_func_array(array($this, 'parent::' . $method), $arguments); } } diff --git a/system/src/Grav/Common/Page/Medium/VideoMedium.php b/system/src/Grav/Common/Page/Medium/VideoMedium.php index 9f477d261..b3c8eed85 100644 --- a/system/src/Grav/Common/Page/Medium/VideoMedium.php +++ b/system/src/Grav/Common/Page/Medium/VideoMedium.php @@ -2,7 +2,7 @@ /** * @package Grav.Common.Page * - * @copyright Copyright (C) 2014 - 2017 RocketTheme, LLC. All rights reserved. + * @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -38,14 +38,12 @@ protected function sourceParsedownElement(array $attributes, $reset = true) */ public function controls($display = true) { - if($display) - { + if($display) { $this->attributes['controls'] = true; - } - else - { + } else { unset($this->attributes['controls']); } + return $this; } @@ -58,6 +56,7 @@ public function controls($display = true) public function poster($urlImage) { $this->attributes['poster'] = $urlImage; + return $this; } @@ -69,14 +68,12 @@ public function poster($urlImage) */ public function loop($status = false) { - if($status) - { + if($status) { $this->attributes['loop'] = true; - } - else - { + } else { unset($this->attributes['loop']); } + return $this; } @@ -88,14 +85,12 @@ public function loop($status = false) */ public function autoplay($status = false) { - if($status) - { + if($status) { $this->attributes['autoplay'] = true; - } - else - { + } else { unset($this->attributes['autoplay']); } + return $this; } @@ -109,6 +104,7 @@ public function reset() parent::reset(); $this->attributes['controls'] = true; + return $this; } } diff --git a/system/src/Grav/Common/Page/Page.php b/system/src/Grav/Common/Page/Page.php index 3afd7d219..517ac44cf 100644 --- a/system/src/Grav/Common/Page/Page.php +++ b/system/src/Grav/Common/Page/Page.php @@ -2,7 +2,7 @@ /** * @package Grav.Common.Page * - * @copyright Copyright (C) 2014 - 2017 RocketTheme, LLC. All rights reserved. + * @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -1109,7 +1109,7 @@ public function toArray() */ public function toYaml() { - return Yaml::dump($this->toArray(), 10); + return Yaml::dump($this->toArray(), 20); } /** @@ -1525,12 +1525,13 @@ public function metadata($var = null) $separator = strpos($key, ':'); $hasSeparator = $separator && $separator < strlen($key) - 1; $entry = [ - 'name' => $key, 'content' => htmlspecialchars($value, ENT_QUOTES, 'UTF-8') ]; - if ($hasSeparator) { + if ($hasSeparator && !Utils::startsWith($key, 'twitter')) { $entry['property'] = $key; + } else { + $entry['name'] = $key; } $this->metadata[$key] = $entry; @@ -2471,7 +2472,15 @@ public function collection($params = 'content', $pagination = true) return new Collection(); } - $collection = $this->evaluate($params['items']); + // See if require published filter is set and use that, if assume published=true + $only_published = true; + if (isset($params['filter']['published']) && $params['filter']['published']) { + $only_published = false; + } elseif (isset($params['filter']['non-published']) && $params['filter']['non-published']) { + $only_published = false; + } + + $collection = $this->evaluate($params['items'], $only_published); if (!$collection instanceof Collection) { $collection = new Collection(); } @@ -2510,25 +2519,60 @@ public function collection($params = 'content', $pagination = true) // If a filter or filters are set, filter the collection... if (isset($params['filter'])) { + + // remove any inclusive sets from filer: + $sets = ['published', 'visible', 'modular', 'routable']; + foreach ($sets as $type) { + if (isset($params['filter'][$type]) && isset($params['filter']['non-'.$type])) { + if ($params['filter'][$type] && $params['filter']['non-'.$type]) { + unset ($params['filter'][$type]); + unset ($params['filter']['non-'.$type]); + } + + } + } + foreach ((array)$params['filter'] as $type => $filter) { switch ($type) { + case 'published': + if ((bool) $filter) { + $collection->published(); + } + break; + case 'non-published': + if ((bool) $filter) { + $collection->nonPublished(); + } + break; case 'visible': - $collection->visible($filter); + if ((bool) $filter) { + $collection->visible(); + } break; case 'non-visible': - $collection->nonVisible($filter); + if ((bool) $filter) { + $collection->nonVisible(); + } break; case 'modular': - $collection->modular($filter); + if ((bool) $filter) { + $collection->modular(); + } break; case 'non-modular': - $collection->nonModular($filter); + if ((bool) $filter) { + $collection->nonModular(); + } break; case 'routable': - $collection->routable($filter); + if ((bool) $filter) { + $collection->routable(); + } break; case 'non-routable': - $collection->nonRoutable($filter); + if ((bool) $filter) { + $collection->nonRoutable(); + } break; case 'type': $collection->ofType($filter); @@ -2589,11 +2633,11 @@ public function collection($params = 'content', $pagination = true) /** * @param string|array $value - * + * @param bool $only_published * @return mixed * @internal */ - public function evaluate($value) + public function evaluate($value, $only_published = true) { // Parse command. if (is_string($value)) { @@ -2662,7 +2706,7 @@ public function evaluate($value) } } - $results = $results->published(); + break; case 'page@': @@ -2706,16 +2750,14 @@ public function evaluate($value) $results = $page->children()->nonModular(); } - $results = $results->published(); - break; case 'root@': case '@root': if (!empty($parts) && $parts[0] === 'descendants') { - $results = $pages->all($pages->root())->nonModular()->published(); + $results = $pages->all($pages->root())->nonModular(); } else { - $results = $pages->root()->children()->nonModular()->published(); + $results = $pages->root()->children()->nonModular(); } break; @@ -2732,10 +2774,14 @@ public function evaluate($value) if (!empty($parts)) { $params = [implode('.', $parts) => $params]; } - $results = $taxonomy_map->findTaxonomy($params)->published(); + $results = $taxonomy_map->findTaxonomy($params); break; } + if ($only_published) { + $results = $results->published(); + } + return $results; } diff --git a/system/src/Grav/Common/Page/Pages.php b/system/src/Grav/Common/Page/Pages.php index 10911efff..78d6109ee 100644 --- a/system/src/Grav/Common/Page/Pages.php +++ b/system/src/Grav/Common/Page/Pages.php @@ -2,7 +2,7 @@ /** * @package Grav.Common.Page * - * @copyright Copyright (C) 2014 - 2017 RocketTheme, LLC. All rights reserved. + * @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -1203,19 +1203,19 @@ protected function buildSort($path, array $pages, $order_by = 'default', $manual break; case 'date': $list[$key] = $child->date(); - $sort_flags = SORT_REGULAR; + $sort_flags = SORT_NUMERIC; break; case 'modified': $list[$key] = $child->modified(); - $sort_flags = SORT_REGULAR; + $sort_flags = SORT_NUMERIC; break; case 'publish_date': $list[$key] = $child->publishDate(); - $sort_flags = SORT_REGULAR; + $sort_flags = SORT_NUMERIC; break; case 'unpublish_date': $list[$key] = $child->unpublishDate(); - $sort_flags = SORT_REGULAR; + $sort_flags = SORT_NUMERIC; break; case 'slug': $list[$key] = $child->slug(); diff --git a/system/src/Grav/Common/Page/Types.php b/system/src/Grav/Common/Page/Types.php index c027142b6..436d4f835 100644 --- a/system/src/Grav/Common/Page/Types.php +++ b/system/src/Grav/Common/Page/Types.php @@ -2,7 +2,7 @@ /** * @package Grav.Common.Page * - * @copyright Copyright (C) 2014 - 2017 RocketTheme, LLC. All rights reserved. + * @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Plugin.php b/system/src/Grav/Common/Plugin.php index dbf530891..ec5b34616 100644 --- a/system/src/Grav/Common/Plugin.php +++ b/system/src/Grav/Common/Plugin.php @@ -2,7 +2,7 @@ /** * @package Grav.Common * - * @copyright Copyright (C) 2014 - 2017 RocketTheme, LLC. All rights reserved. + * @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -90,7 +90,7 @@ public function setConfig(Config $config) /** * Get configuration of the plugin. * - * @return Config + * @return array */ public function config() { @@ -121,7 +121,7 @@ protected function isPluginActiveAdmin($plugin_route) if (strpos($uri->path(), $this->config->get('plugins.admin.route') . '/' . $plugin_route) === false) { $should_run = false; - } elseif (isset($uri->paths()[1]) && $uri->paths()[1] == $plugin_route) { + } elseif (isset($uri->paths()[1]) && $uri->paths()[1] === $plugin_route) { $should_run = true; } @@ -207,6 +207,7 @@ public function offsetGet($offset) * * @param mixed $offset The offset to assign the value to. * @param mixed $value The value to set. + * @throws LogicException */ public function offsetSet($offset, $value) { @@ -217,6 +218,7 @@ public function offsetSet($offset, $value) * Unsets an offset. * * @param mixed $offset The offset to unset. + * @throws LogicException */ public function offsetUnset($offset) { @@ -299,13 +301,14 @@ protected function mergeConfig(Page $page, $deep = false, $params = [], $type = */ private function mergeArrays($deep = false, $array1, $array2) { - if ($deep == 'merge') { + if ($deep === 'merge') { return Utils::arrayMergeRecursiveUnique($array1, $array2); - } elseif ($deep === true) { + } + if ($deep === true) { return array_replace_recursive($array1, $array2); - } else { - return array_merge($array1, $array2); } + + return array_merge($array1, $array2); } /** diff --git a/system/src/Grav/Common/Plugins.php b/system/src/Grav/Common/Plugins.php index 25fb356ad..5e407a905 100644 --- a/system/src/Grav/Common/Plugins.php +++ b/system/src/Grav/Common/Plugins.php @@ -2,7 +2,7 @@ /** * @package Grav.Common * - * @copyright Copyright (C) 2014 - 2017 RocketTheme, LLC. All rights reserved. + * @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Processors/AssetsProcessor.php b/system/src/Grav/Common/Processors/AssetsProcessor.php index 32a288934..6ca952d70 100644 --- a/system/src/Grav/Common/Processors/AssetsProcessor.php +++ b/system/src/Grav/Common/Processors/AssetsProcessor.php @@ -2,7 +2,7 @@ /** * @package Grav.Common.Processors * - * @copyright Copyright (C) 2014 - 2017 RocketTheme, LLC. All rights reserved. + * @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Processors/ConfigurationProcessor.php b/system/src/Grav/Common/Processors/ConfigurationProcessor.php index ed3adcd05..03478532c 100644 --- a/system/src/Grav/Common/Processors/ConfigurationProcessor.php +++ b/system/src/Grav/Common/Processors/ConfigurationProcessor.php @@ -2,7 +2,7 @@ /** * @package Grav.Common.Processors * - * @copyright Copyright (C) 2014 - 2017 RocketTheme, LLC. All rights reserved. + * @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Processors/DebuggerAssetsProcessor.php b/system/src/Grav/Common/Processors/DebuggerAssetsProcessor.php index 1c048cbcb..643e8d13f 100644 --- a/system/src/Grav/Common/Processors/DebuggerAssetsProcessor.php +++ b/system/src/Grav/Common/Processors/DebuggerAssetsProcessor.php @@ -2,7 +2,7 @@ /** * @package Grav.Common.Processors * - * @copyright Copyright (C) 2014 - 2017 RocketTheme, LLC. All rights reserved. + * @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Processors/DebuggerInitProcessor.php b/system/src/Grav/Common/Processors/DebuggerInitProcessor.php index 95695fe02..e3b4e1d6f 100644 --- a/system/src/Grav/Common/Processors/DebuggerInitProcessor.php +++ b/system/src/Grav/Common/Processors/DebuggerInitProcessor.php @@ -2,7 +2,7 @@ /** * @package Grav.Common.Processors * - * @copyright Copyright (C) 2014 - 2017 RocketTheme, LLC. All rights reserved. + * @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Processors/ErrorsProcessor.php b/system/src/Grav/Common/Processors/ErrorsProcessor.php index 16afffe44..7c7685da2 100644 --- a/system/src/Grav/Common/Processors/ErrorsProcessor.php +++ b/system/src/Grav/Common/Processors/ErrorsProcessor.php @@ -2,7 +2,7 @@ /** * @package Grav.Common.Processors * - * @copyright Copyright (C) 2014 - 2017 RocketTheme, LLC. All rights reserved. + * @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Processors/InitializeProcessor.php b/system/src/Grav/Common/Processors/InitializeProcessor.php index 734eb8211..e3f25691c 100644 --- a/system/src/Grav/Common/Processors/InitializeProcessor.php +++ b/system/src/Grav/Common/Processors/InitializeProcessor.php @@ -2,7 +2,7 @@ /** * @package Grav.Common.Processors * - * @copyright Copyright (C) 2014 - 2017 RocketTheme, LLC. All rights reserved. + * @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -31,8 +31,12 @@ public function process() date_default_timezone_set($this->container['config']->get('system.timezone')); } - // Initialize uri, session. - $this->container['session']->init(); + // 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'])) { + $this->container['session']->init(); + } + + // Initialize uri. $this->container['uri']->init(); $this->container->setLocale(); diff --git a/system/src/Grav/Common/Processors/PagesProcessor.php b/system/src/Grav/Common/Processors/PagesProcessor.php index 5bb003954..b2828f796 100644 --- a/system/src/Grav/Common/Processors/PagesProcessor.php +++ b/system/src/Grav/Common/Processors/PagesProcessor.php @@ -2,7 +2,7 @@ /** * @package Grav.Common.Processors * - * @copyright Copyright (C) 2014 - 2017 RocketTheme, LLC. All rights reserved. + * @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Processors/PluginsProcessor.php b/system/src/Grav/Common/Processors/PluginsProcessor.php index 02a28d5f7..ee563930e 100644 --- a/system/src/Grav/Common/Processors/PluginsProcessor.php +++ b/system/src/Grav/Common/Processors/PluginsProcessor.php @@ -2,7 +2,7 @@ /** * @package Grav.Common.Processors * - * @copyright Copyright (C) 2014 - 2017 RocketTheme, LLC. All rights reserved. + * @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Processors/ProcessorBase.php b/system/src/Grav/Common/Processors/ProcessorBase.php index c53a989c7..056c86d6a 100644 --- a/system/src/Grav/Common/Processors/ProcessorBase.php +++ b/system/src/Grav/Common/Processors/ProcessorBase.php @@ -2,7 +2,7 @@ /** * @package Grav.Common.Processors * - * @copyright Copyright (C) 2014 - 2017 RocketTheme, LLC. All rights reserved. + * @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Processors/ProcessorInterface.php b/system/src/Grav/Common/Processors/ProcessorInterface.php index e534dc8b7..0e4b1694b 100644 --- a/system/src/Grav/Common/Processors/ProcessorInterface.php +++ b/system/src/Grav/Common/Processors/ProcessorInterface.php @@ -2,7 +2,7 @@ /** * @package Grav.Common.Processors * - * @copyright Copyright (C) 2014 - 2017 RocketTheme, LLC. All rights reserved. + * @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Processors/RenderProcessor.php b/system/src/Grav/Common/Processors/RenderProcessor.php index 5171c507d..b5f281918 100644 --- a/system/src/Grav/Common/Processors/RenderProcessor.php +++ b/system/src/Grav/Common/Processors/RenderProcessor.php @@ -2,7 +2,7 @@ /** * @package Grav.Common.Processors * - * @copyright Copyright (C) 2014 - 2017 RocketTheme, LLC. All rights reserved. + * @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Processors/SiteSetupProcessor.php b/system/src/Grav/Common/Processors/SiteSetupProcessor.php index b2a5e9df8..1f3f8afec 100644 --- a/system/src/Grav/Common/Processors/SiteSetupProcessor.php +++ b/system/src/Grav/Common/Processors/SiteSetupProcessor.php @@ -2,7 +2,7 @@ /** * @package Grav.Common.Processors * - * @copyright Copyright (C) 2014 - 2017 RocketTheme, LLC. All rights reserved. + * @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Processors/TasksProcessor.php b/system/src/Grav/Common/Processors/TasksProcessor.php index 8c41de783..d1e7a2f92 100644 --- a/system/src/Grav/Common/Processors/TasksProcessor.php +++ b/system/src/Grav/Common/Processors/TasksProcessor.php @@ -2,7 +2,7 @@ /** * @package Grav.Common.Processors * - * @copyright Copyright (C) 2014 - 2017 RocketTheme, LLC. All rights reserved. + * @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Processors/ThemesProcessor.php b/system/src/Grav/Common/Processors/ThemesProcessor.php index b0b79d995..c9ea013e7 100644 --- a/system/src/Grav/Common/Processors/ThemesProcessor.php +++ b/system/src/Grav/Common/Processors/ThemesProcessor.php @@ -2,7 +2,7 @@ /** * @package Grav.Common.Processors * - * @copyright Copyright (C) 2014 - 2017 RocketTheme, LLC. All rights reserved. + * @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Processors/TwigProcessor.php b/system/src/Grav/Common/Processors/TwigProcessor.php index 9867efc1d..392824f90 100644 --- a/system/src/Grav/Common/Processors/TwigProcessor.php +++ b/system/src/Grav/Common/Processors/TwigProcessor.php @@ -2,7 +2,7 @@ /** * @package Grav.Common.Processors * - * @copyright Copyright (C) 2014 - 2017 RocketTheme, LLC. All rights reserved. + * @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Service/AssetsServiceProvider.php b/system/src/Grav/Common/Service/AssetsServiceProvider.php index 91a7a86c3..961801397 100644 --- a/system/src/Grav/Common/Service/AssetsServiceProvider.php +++ b/system/src/Grav/Common/Service/AssetsServiceProvider.php @@ -2,7 +2,7 @@ /** * @package Grav.Common.Service * - * @copyright Copyright (C) 2014 - 2017 RocketTheme, LLC. All rights reserved. + * @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Service/ConfigServiceProvider.php b/system/src/Grav/Common/Service/ConfigServiceProvider.php index ad3bf07f9..25c1a126e 100644 --- a/system/src/Grav/Common/Service/ConfigServiceProvider.php +++ b/system/src/Grav/Common/Service/ConfigServiceProvider.php @@ -2,7 +2,7 @@ /** * @package Grav.Common.Service * - * @copyright Copyright (C) 2014 - 2017 RocketTheme, LLC. All rights reserved. + * @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Service/ErrorServiceProvider.php b/system/src/Grav/Common/Service/ErrorServiceProvider.php index ec87562d5..139de54d5 100644 --- a/system/src/Grav/Common/Service/ErrorServiceProvider.php +++ b/system/src/Grav/Common/Service/ErrorServiceProvider.php @@ -2,7 +2,7 @@ /** * @package Grav.Common.Service * - * @copyright Copyright (C) 2014 - 2017 RocketTheme, LLC. All rights reserved. + * @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Service/LoggerServiceProvider.php b/system/src/Grav/Common/Service/LoggerServiceProvider.php index 6c61685c9..628d15897 100644 --- a/system/src/Grav/Common/Service/LoggerServiceProvider.php +++ b/system/src/Grav/Common/Service/LoggerServiceProvider.php @@ -2,7 +2,7 @@ /** * @package Grav.Common.Service * - * @copyright Copyright (C) 2014 - 2017 RocketTheme, LLC. All rights reserved. + * @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Service/MessagesServiceProvider.php b/system/src/Grav/Common/Service/MessagesServiceProvider.php deleted file mode 100644 index 60b3815fe..000000000 --- a/system/src/Grav/Common/Service/MessagesServiceProvider.php +++ /dev/null @@ -1,30 +0,0 @@ -messages)) { - $session->messages = new Message; - } - - return $session->messages; - }; - } -} diff --git a/system/src/Grav/Common/Service/OutputServiceProvider.php b/system/src/Grav/Common/Service/OutputServiceProvider.php index 92871fd2a..89322ee49 100644 --- a/system/src/Grav/Common/Service/OutputServiceProvider.php +++ b/system/src/Grav/Common/Service/OutputServiceProvider.php @@ -2,7 +2,7 @@ /** * @package Grav.Common.Service * - * @copyright Copyright (C) 2014 - 2017 RocketTheme, LLC. All rights reserved. + * @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Service/PageServiceProvider.php b/system/src/Grav/Common/Service/PageServiceProvider.php index b880a8467..bf55f791b 100644 --- a/system/src/Grav/Common/Service/PageServiceProvider.php +++ b/system/src/Grav/Common/Service/PageServiceProvider.php @@ -2,7 +2,7 @@ /** * @package Grav.Common.Service * - * @copyright Copyright (C) 2014 - 2017 RocketTheme, LLC. All rights reserved. + * @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Service/SessionServiceProvider.php b/system/src/Grav/Common/Service/SessionServiceProvider.php new file mode 100644 index 000000000..52e107b7a --- /dev/null +++ b/system/src/Grav/Common/Service/SessionServiceProvider.php @@ -0,0 +1,105 @@ +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); + + // Activate admin if we're inside the admin path. + $is_admin = false; + if ($config->get('plugins.admin.enabled')) { + $base = '/' . trim($config->get('plugins.admin.route'), '/'); + + // Uri::route() is not processed yet, let's quickly get what we need. + $current_route = str_replace(Uri::filterPath($uri->rootUrl(false)), '', parse_url($uri->url(true), PHP_URL_PATH)); + + // 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); + $enabled = $is_admin = true; + } + } + + // Fix for HUGE session timeouts. + if ($session_timeout > 99999999999) { + $session_timeout = 9999999999; + } + + $inflector = new Inflector(); + $session_name = $inflector->hyphenize($config->get('system.session.name', 'grav_site')) . '-' . substr(md5(GRAV_ROOT), 0, 7); + if ($is_admin && $config->get('system.session.split', true)) { + $session_name .= '-admin'; + } + + // Define session service. + $session = new Session($session_timeout, $session_path, $domain); + $session->setName($session_name); + $session->setSecure($secure); + $session->setHttpOnly($httponly); + $session->setAutoStart($enabled); + + return $session; + }; + + // Define session message service. + $container['messages'] = function ($c) { + if (!isset($c['session']) || !$c['session']->started()) { + /** @var Debugger $debugger */ + $debugger = $c['debugger']; + $debugger->addMessage('Inactive session: session messages may disappear', 'warming'); + + return new Message; + } + + /** @var Session $session */ + $session = $c['session']; + + if (!isset($session->messages)) { + $session->messages = new Message; + } + + return $session->messages; + }; + } +} diff --git a/system/src/Grav/Common/Service/StreamsServiceProvider.php b/system/src/Grav/Common/Service/StreamsServiceProvider.php index cb0638839..a2ebcc671 100644 --- a/system/src/Grav/Common/Service/StreamsServiceProvider.php +++ b/system/src/Grav/Common/Service/StreamsServiceProvider.php @@ -2,7 +2,7 @@ /** * @package Grav.Common.Service * - * @copyright Copyright (C) 2014 - 2017 RocketTheme, LLC. All rights reserved. + * @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Service/TaskServiceProvider.php b/system/src/Grav/Common/Service/TaskServiceProvider.php index b0f90d334..40b9696b4 100644 --- a/system/src/Grav/Common/Service/TaskServiceProvider.php +++ b/system/src/Grav/Common/Service/TaskServiceProvider.php @@ -2,7 +2,7 @@ /** * @package Grav.Common.Service * - * @copyright Copyright (C) 2014 - 2017 RocketTheme, LLC. All rights reserved. + * @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Session.php b/system/src/Grav/Common/Session.php index cbf2477b7..87f7e83b8 100644 --- a/system/src/Grav/Common/Session.php +++ b/system/src/Grav/Common/Session.php @@ -2,7 +2,7 @@ /** * @package Grav.Common * - * @copyright Copyright (C) 2014 - 2017 RocketTheme, LLC. All rights reserved. + * @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -12,107 +12,106 @@ class Session extends BaseSession { - protected $grav; - protected $session; + /** @var bool */ + protected $autoStart = false; /** - * Session constructor. + * Initialize session. * - * @param Grav $grav + * Code in this function has been moved into SessionServiceProvider class. */ - public function __construct(Grav $grav) + public function init() { - $this->grav = $grav; + if ($this->autoStart) { + $this->start(); + + $this->autoStart = false; + } } /** - * Session init + * @param bool $auto + * @return $this */ - public function init() + public function setAutoStart($auto) { - /** @var Uri $uri */ - $uri = $this->grav['uri']; - $config = $this->grav['config']; + $this->autoStart = (bool)$auto; - $is_admin = false; - $base_url = $uri->rootUrl(false); + return $this; + } - $session_timeout = $config->get('system.session.timeout', 1800); - $session_path = $config->get('system.session.path'); - if (!$session_path) { - $session_path = '/' . ltrim($base_url, '/'); - } + /** + * @param bool $secure + * @return $this + */ + public function setSecure($secure) + { + ini_set('session.cookie_secure', (bool)$secure); - // Activate admin if we're inside the admin path. - if ($config->get('plugins.admin.enabled')) { - $route = $config->get('plugins.admin.route'); - // Uri::route() is not processed yet, let's quickly get what we need - $current_route = str_replace($base_url, '', parse_url($uri->url(true), PHP_URL_PATH)); - $base = '/' . trim($route, '/'); - - if (substr($current_route, 0, strlen($base)) == $base || //handle no language specified - substr($current_route, 3, strlen($base)) == $base || //handle language (en) - substr($current_route, 6, strlen($base)) == $base) { //handle region specific language prefix (en-US) - $session_timeout = $config->get('plugins.admin.session.timeout', 1800); - $is_admin = true; - } - } + return $this; + } - if ($config->get('system.session.enabled') || $is_admin) { - $domain = $uri->host(); - if ($domain === 'localhost') { - $domain = ''; - } - - // Fix for HUGE session timeouts - if ($session_timeout > 99999999999) { - $session_timeout = 9999999999; - } - - // Define session service. - parent::__construct($session_timeout, $session_path, $domain); - - $secure = $config->get('system.session.secure', false); - $httponly = $config->get('system.session.httponly', true); - - $unique_identifier = GRAV_ROOT; - $inflector = new Inflector(); - $session_name = $inflector->hyphenize($config->get('system.session.name', 'grav_site')) . '-' . substr(md5($unique_identifier), 0, 7); - $split_session = $config->get('system.session.split', true); - if ($is_admin && $split_session) { - $session_name .= '-admin'; - } - $this->setName($session_name); - ini_set('session.cookie_secure', $secure); - ini_set('session.cookie_httponly', $httponly); - $this->start(); - setcookie(session_name(), session_id(), $session_timeout ? time() + $session_timeout : 0, $session_path, $domain, $secure, $httponly); - } + /** + * @param bool $httponly + * @return $this + */ + public function setHttpOnly($httponly) + { + ini_set('session.cookie_httponly', (bool)$httponly); + + return $this; } - // Store something in session temporarily + /** + * Store something in session temporarily. + * + * @param string $name + * @param mixed $object + * @return $this + */ public function setFlashObject($name, $object) { - $this->$name = serialize($object); + $this->{$name} = serialize($object); + + return $this; } - // Return object and remove it from session + /** + * Return object and remove it from session. + * + * @param string $name + * @return mixed + */ public function getFlashObject($name) { - $object = unserialize($this->$name); + $object = unserialize($this->{$name}); - $this->$name = null; + $this->{$name} = null; return $object; } - // Store something in cookie temporarily + /** + * Store something in cookie temporarily. + * + * @param string $name + * @param mixed $object + * @param int $time + * @return $this + */ public function setFlashCookieObject($name, $object, $time = 60) { setcookie($name, json_encode($object), time() + $time, '/'); + + return $this; } - // Return object and remove it from the cookie + /** + * Return object and remove it from the cookie. + * + * @param string $name + * @return mixed|null + */ public function getFlashCookieObject($name) { if (isset($_COOKIE[$name])) { @@ -120,5 +119,7 @@ public function getFlashCookieObject($name) setcookie($name, '', time() - 3600, '/'); return $object; } + + return null; } } diff --git a/system/src/Grav/Common/Taxonomy.php b/system/src/Grav/Common/Taxonomy.php index ec9f4f779..dda284412 100644 --- a/system/src/Grav/Common/Taxonomy.php +++ b/system/src/Grav/Common/Taxonomy.php @@ -2,7 +2,7 @@ /** * @package Grav.Common * - * @copyright Copyright (C) 2014 - 2017 RocketTheme, LLC. All rights reserved. + * @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Theme.php b/system/src/Grav/Common/Theme.php index 1cff2c1b4..537df11d6 100644 --- a/system/src/Grav/Common/Theme.php +++ b/system/src/Grav/Common/Theme.php @@ -2,7 +2,7 @@ /** * @package Grav.Common * - * @copyright Copyright (C) 2014 - 2017 RocketTheme, LLC. All rights reserved. + * @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Themes.php b/system/src/Grav/Common/Themes.php index 29324be9a..8f29037dd 100644 --- a/system/src/Grav/Common/Themes.php +++ b/system/src/Grav/Common/Themes.php @@ -2,7 +2,7 @@ /** * @package Grav.Common * - * @copyright Copyright (C) 2014 - 2017 RocketTheme, LLC. All rights reserved. + * @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Twig/Node/TwigNodeMarkdown.php b/system/src/Grav/Common/Twig/Node/TwigNodeMarkdown.php new file mode 100644 index 000000000..5943c952a --- /dev/null +++ b/system/src/Grav/Common/Twig/Node/TwigNodeMarkdown.php @@ -0,0 +1,35 @@ + $body), array(), $lineno, $tag); + } + /** + * Compiles the node to PHP. + * + * @param \Twig_Compiler A Twig_Compiler instance + */ + public function compile(\Twig_Compiler $compiler) + { + $compiler + ->addDebugInfo($this) + ->write('ob_start();' . PHP_EOL) + ->subcompile($this->getNode('body')) + ->write('$content = ob_get_clean();' . PHP_EOL) + ->write('preg_match("/^\s*/", $content, $matches);' . PHP_EOL) + ->write('$lines = explode("\n", $content);' . PHP_EOL) + ->write('$content = preg_replace(\'/^\' . $matches[0]. \'/\', "", $lines);' . PHP_EOL) + ->write('$content = join("\n", $content);' . PHP_EOL) + ->write('echo $this->env->getExtension(\'Grav\Common\Twig\TwigExtension\')->markdownFunction($content);' . PHP_EOL); + } +} diff --git a/system/src/Grav/Common/Twig/Node/TwigNodeScript.php b/system/src/Grav/Common/Twig/Node/TwigNodeScript.php new file mode 100644 index 000000000..35ca20f7a --- /dev/null +++ b/system/src/Grav/Common/Twig/Node/TwigNodeScript.php @@ -0,0 +1,102 @@ + $body, 'file' => $file, 'group' => $group, 'priority' => $priority, 'attributes' => $attributes], [], $lineno, $tag); + } + /** + * Compiles the node to PHP. + * + * @param \Twig_Compiler $compiler A Twig_Compiler instance + * @throws \LogicException + */ + public function compile(\Twig_Compiler $compiler) + { + $compiler->addDebugInfo($this); + + if ($this->getNode('attributes') !== null) { + $compiler + ->write('$attributes = ') + ->subcompile($this->getNode('attributes')) + ->raw(";\n") + ->write("if (\$attributes !== null && !is_array(\$attributes)) {\n") + ->indent() + ->write("throw new UnexpectedValueException('{% {$this->tagName} with x %}: x is not an array');\n") + ->outdent() + ->write("}\n"); + } else { + $compiler->write('$attributes = [];' . "\n"); + } + + if ($this->getNode('group') !== null) { + $compiler + ->write('$group = ') + ->subcompile($this->getNode('group')) + ->raw(";\n") + ->write("if (\$group !== null && !is_string(\$group)) {\n") + ->indent() + ->write("throw new UnexpectedValueException('{% {$this->tagName} in x %}: x is not a string');\n") + ->outdent() + ->write("}\n"); + } else { + $compiler->write('$group = null;' . "\n"); + } + + if ($this->getNode('priority') !== null) { + $compiler + ->write('$priority = (int)(') + ->subcompile($this->getNode('priority')) + ->raw(");\n"); + } else { + $compiler->write('$priority = null;' . "\n"); + } + + $compiler->write("\$assets = \\Grav\\Common\\Grav::instance()['assets'];\n"); + + if ($this->getNode('file') !== null) { + $compiler + ->write('$file = ') + ->subcompile($this->getNode('file')) + ->write(";\n") + ->write("\$pipeline = !empty(\$attributes['pipeline']);\n") + ->write("\$loading = !empty(\$attributes['defer']) ? 'defer' : (!empty(\$attributes['async']) ? 'async' : null);\n") + ->write("\$assets->addJs(\$file, \$priority, \$pipeline, \$loading, \$group);\n"); + } else { + $compiler + ->write("ob_start();\n") + ->subcompile($this->getNode('body')) + ->write("\$content = ob_get_clean();") + ->write("\$assets->addInlineJs(\$content, \$priority, \$group, \$attributes);\n"); + } + } +} diff --git a/system/src/Grav/Common/Twig/Node/TwigNodeStyle.php b/system/src/Grav/Common/Twig/Node/TwigNodeStyle.php new file mode 100644 index 000000000..8c0b3b87c --- /dev/null +++ b/system/src/Grav/Common/Twig/Node/TwigNodeStyle.php @@ -0,0 +1,98 @@ + $body, 'file' => $file, 'group' => $group, 'priority' => $priority, 'attributes' => $attributes], [], $lineno, $tag); + } + /** + * Compiles the node to PHP. + * + * @param \Twig_Compiler $compiler A Twig_Compiler instance + * @throws \LogicException + */ + public function compile(\Twig_Compiler $compiler) + { + $compiler->addDebugInfo($this); + + if ($this->getNode('attributes') !== null) { + $compiler + ->write('$attributes = ') + ->subcompile($this->getNode('attributes')) + ->raw(";\n") + ->write("if (\$attributes !== null && !is_array(\$attributes)) {\n") + ->indent() + ->write("throw new UnexpectedValueException('{% {$this->tagName} with x %}: x is not an array');\n") + ->outdent() + ->write("}\n"); + } else { + $compiler->write('$attributes = [];' . "\n"); + } + + if ($this->getNode('group') !== null) { + $compiler + ->write('$group = ') + ->subcompile($this->getNode('group')) + ->raw(";\n") + ->write("if (\$group !== null && !is_string(\$group)) {\n") + ->indent() + ->write("throw new UnexpectedValueException('{% {$this->tagName} in x %}: x is not a string');\n") + ->outdent() + ->write("}\n"); + } else { + $compiler->write('$group = null;' . "\n"); + } + + if ($this->getNode('priority') !== null) { + $compiler + ->write('$priority = (int)(') + ->subcompile($this->getNode('priority')) + ->raw(");\n"); + } else { + $compiler->write('$priority = null;' . "\n"); + } + + $compiler->write("\$assets = \\Grav\\Common\\Grav::instance()['assets'];\n"); + + if ($this->getNode('file') !== null) { + $compiler + ->write('$file = ') + ->subcompile($this->getNode('file')) + ->write(";\n") + ->write("\$pipeline = !empty(\$attributes['pipeline']);\n") + ->write("\$assets->addCss(\$file, \$priority, \$pipeline, \$group);\n"); + } else { + $compiler + ->write("ob_start();\n") + ->subcompile($this->getNode('body')) + ->write("\$content = ob_get_clean();") + ->write("\$assets->addInlineCss(\$content, \$priority, \$group);\n"); + } + } +} diff --git a/system/src/Grav/Common/Twig/Node/TwigNodeSwitch.php b/system/src/Grav/Common/Twig/Node/TwigNodeSwitch.php new file mode 100644 index 000000000..e0e812700 --- /dev/null +++ b/system/src/Grav/Common/Twig/Node/TwigNodeSwitch.php @@ -0,0 +1,71 @@ + $value, 'cases' => $cases, 'default' => $default), array(), $lineno, $tag); + } + + /** + * Compiles the node to PHP. + * + * @param \Twig_Compiler A Twig_Compiler instance + */ + public function compile(\Twig_Compiler $compiler) + { + $compiler + ->addDebugInfo($this) + ->write("switch (") + ->subcompile($this->getNode('value')) + ->raw(") {\n") + ->indent(); + + foreach ($this->getNode('cases') as $case) + { + if (!$case->hasNode('body')) + { + continue; + } + + foreach ($case->getNode('values') as $value) + { + $compiler + ->write('case ') + ->subcompile($value) + ->raw(":\n"); + } + + $compiler + ->write("{\n") + ->indent() + ->subcompile($case->getNode('body')) + ->write("break;\n") + ->outdent() + ->write("}\n"); + } + + if ($this->hasNode('default') && $this->getNode('default') !== null) + { + $compiler + ->write("default:\n") + ->write("{\n") + ->indent() + ->subcompile($this->getNode('default')) + ->outdent() + ->write("}\n"); + } + + $compiler + ->outdent() + ->write("}\n"); + } +} diff --git a/system/src/Grav/Common/Twig/Node/TwigNodeTryCatch.php b/system/src/Grav/Common/Twig/Node/TwigNodeTryCatch.php new file mode 100644 index 000000000..e37c31992 --- /dev/null +++ b/system/src/Grav/Common/Twig/Node/TwigNodeTryCatch.php @@ -0,0 +1,52 @@ + $try, 'catch' => $catch), array(), $lineno, $tag); + } + + /** + * Compiles the node to PHP. + * + * @param \Twig_Compiler $compiler A Twig_Compiler instance + * @throws \LogicException + */ + public function compile(\Twig_Compiler $compiler) + { + $compiler->addDebugInfo($this); + + $compiler + ->write('try {') + ; + + $compiler + ->indent() + ->subcompile($this->getNode('try')) + ; + + if ($this->hasNode('catch') && null !== $this->getNode('catch')) { + $compiler + ->outdent() + ->write('} catch (\Exception $e) {' . "\n") + ->indent() + ->write('if (isset($context[\'grav\'][\'debugger\'])) $context[\'grav\'][\'debugger\']->addException($e);' . "\n") + ->write('$context[\'e\'] = $e;' . "\n") + ->subcompile($this->getNode('catch')) + ; + } + + $compiler + ->outdent() + ->write("}\n"); + } +} diff --git a/system/src/Grav/Common/Twig/TokenParser/TwigTokenParserMarkdown.php b/system/src/Grav/Common/Twig/TokenParser/TwigTokenParserMarkdown.php new file mode 100644 index 000000000..a1d413530 --- /dev/null +++ b/system/src/Grav/Common/Twig/TokenParser/TwigTokenParserMarkdown.php @@ -0,0 +1,53 @@ +getLine(); + $this->parser->getStream()->expect(\Twig_Token::BLOCK_END_TYPE); + $body = $this->parser->subparse(array($this, 'decideMarkdownEnd'), true); + $this->parser->getStream()->expect(\Twig_Token::BLOCK_END_TYPE); + return new TwigNodeMarkdown($body, $lineno, $this->getTag()); + } + /** + * Decide if current token marks end of Markdown block. + * + * @param \Twig_Token $token + * @return bool + */ + public function decideMarkdownEnd(\Twig_Token $token) + { + return $token->test('endmarkdown'); + } + /** + * {@inheritdoc} + */ + public function getTag() + { + return 'markdown'; + } +} diff --git a/system/src/Grav/Common/Twig/TokenParser/TwigTokenParserScript.php b/system/src/Grav/Common/Twig/TokenParser/TwigTokenParserScript.php new file mode 100644 index 000000000..98c72e21b --- /dev/null +++ b/system/src/Grav/Common/Twig/TokenParser/TwigTokenParserScript.php @@ -0,0 +1,100 @@ +getLine(); + $stream = $this->parser->getStream(); + + list($file, $group, $priority, $attributes) = $this->parseArguments($token); + + $content = null; + if ($file === null) { + $content = $this->parser->subparse([$this, 'decideBlockEnd'], true); + $stream->expect(\Twig_Token::BLOCK_END_TYPE); + } + + return new TwigNodeScript($content, $file, $group, $priority, $attributes, $lineno, $this->getTag()); + } + + /** + * @param \Twig_Token $token + * @return array + */ + protected function parseArguments(\Twig_Token $token) + { + $stream = $this->parser->getStream(); + + $file = null; + if (!$stream->test(\Twig_Token::NAME_TYPE) && !$stream->test(\Twig_Token::OPERATOR_TYPE) && !$stream->test(\Twig_Token::BLOCK_END_TYPE)) { + $file = $this->parser->getExpressionParser()->parseExpression(); + } + + $group = null; + if ($stream->nextIf(\Twig_Token::OPERATOR_TYPE, 'in')) { + $group = $this->parser->getExpressionParser()->parseExpression(); + } + + $priority = null; + if ($stream->nextIf(\Twig_Token::NAME_TYPE, 'priority')) { + $stream->expect(\Twig_Token::PUNCTUATION_TYPE, ':'); + $priority = $this->parser->getExpressionParser()->parseExpression(); + } + + $attributes = null; + if ($stream->nextIf(\Twig_Token::NAME_TYPE, 'with')) { + $attributes = $this->parser->getExpressionParser()->parseExpression(); + } + + $stream->expect(\Twig_Token::BLOCK_END_TYPE); + + return [$file, $group, $priority, $attributes]; + } + + /** + * @param \Twig_Token $token + * @return bool + */ + public function decideBlockEnd(\Twig_Token $token) + { + return $token->test('endscript'); + } + + /** + * Gets the tag name associated with this token parser. + * + * @return string The tag name + */ + public function getTag() + { + return 'script'; + } +} diff --git a/system/src/Grav/Common/Twig/TokenParser/TwigTokenParserStyle.php b/system/src/Grav/Common/Twig/TokenParser/TwigTokenParserStyle.php new file mode 100644 index 000000000..f207686bd --- /dev/null +++ b/system/src/Grav/Common/Twig/TokenParser/TwigTokenParserStyle.php @@ -0,0 +1,99 @@ +getLine(); + $stream = $this->parser->getStream(); + + list ($file, $group, $priority, $attributes) = $this->parseArguments($token); + + $content = null; + if (!$file) { + $content = $this->parser->subparse([$this, 'decideBlockEnd'], true); + $stream->expect(\Twig_Token::BLOCK_END_TYPE); + } + + return new TwigNodeStyle($content, $file, $group, $priority, $attributes, $lineno, $this->getTag()); + } + + /** + * @param \Twig_Token $token + * @return array + */ + protected function parseArguments(\Twig_Token $token) + { + $stream = $this->parser->getStream(); + + $file = null; + if (!$stream->test(\Twig_Token::NAME_TYPE) && !$stream->test(\Twig_Token::OPERATOR_TYPE) && !$stream->test(\Twig_Token::BLOCK_END_TYPE)) { + $file = $this->parser->getExpressionParser()->parseExpression(); + } + + $group = null; + if ($stream->nextIf(\Twig_Token::OPERATOR_TYPE, 'in')) { + $group = $this->parser->getExpressionParser()->parseExpression(); + } + + $priority = null; + if ($stream->nextIf(\Twig_Token::NAME_TYPE, 'priority')) { + $stream->expect(\Twig_Token::PUNCTUATION_TYPE, ':'); + $priority = $this->parser->getExpressionParser()->parseExpression(); + } + + $attributes = null; + if ($stream->nextIf(\Twig_Token::NAME_TYPE, 'with')) { + $attributes = $this->parser->getExpressionParser()->parseExpression(); + } + + $stream->expect(\Twig_Token::BLOCK_END_TYPE); + + return [$file, $group, $priority, $attributes]; + } + + /** + * @param \Twig_Token $token + * @return bool + */ + public function decideBlockEnd(\Twig_Token $token) + { + return $token->test('endstyle'); + } + + /** + * Gets the tag name associated with this token parser. + * + * @return string The tag name + */ + public function getTag() + { + return 'style'; + } +} diff --git a/system/src/Grav/Common/Twig/TokenParser/TwigTokenParserSwitch.php b/system/src/Grav/Common/Twig/TokenParser/TwigTokenParserSwitch.php new file mode 100644 index 000000000..2da932de4 --- /dev/null +++ b/system/src/Grav/Common/Twig/TokenParser/TwigTokenParserSwitch.php @@ -0,0 +1,138 @@ +getLine(); + $stream = $this->parser->getStream(); + + $name = $this->parser->getExpressionParser()->parseExpression(); + $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()) == '') + { + $stream->next(); + } + + $stream->expect(\Twig_Token::BLOCK_START_TYPE); + + $expressionParser = $this->parser->getExpressionParser(); + + $default = null; + $cases = array(); + $end = false; + + while (!$end) + { + $next = $stream->next(); + + 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; + } + } + + $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; + } + case 'default': + { + $stream->expect(\Twig_Token::BLOCK_END_TYPE); + $default = $this->parser->subparse(array($this, 'decideIfEnd')); + break; + } + case 'endswitch': + { + $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); + } + } + } + + $stream->expect(\Twig_Token::BLOCK_END_TYPE); + + return new TwigNodeSwitch($name, new \Twig_Node($cases), $default, $lineno, $this->getTag()); + } + + /** + * Decide if current token marks switch logic. + * + * @param \Twig_Token $token + * @return bool + */ + public function decideIfFork(\Twig_Token $token) + { + return $token->test(array('case', 'default', 'endswitch')); + } + + /** + * Decide if current token marks end of swtich block. + * + * @param \Twig_Token $token + * @return bool + */ + public function decideIfEnd(\Twig_Token $token) + { + return $token->test(array('endswitch')); + } + + + /** + * {@inheritdoc} + */ + public function getTag() + { + return 'switch'; + } +} diff --git a/system/src/Grav/Common/Twig/TokenParser/TwigTokenParserTryCatch.php b/system/src/Grav/Common/Twig/TokenParser/TwigTokenParserTryCatch.php new file mode 100644 index 000000000..d639c57a0 --- /dev/null +++ b/system/src/Grav/Common/Twig/TokenParser/TwigTokenParserTryCatch.php @@ -0,0 +1,68 @@ + + * {% try %} + *
  • {{ user.get('name') }}
  • + * {% catch %} + * {{ e.message }} + * {% endcatch %} + * + */ +class TwigTokenParserTryCatch extends \Twig_TokenParser +{ + /** + * Parses a token and returns a node. + * + * @param \Twig_Token $token A Twig_Token instance + * + * @return \Twig_NodeInterface A Twig_NodeInterface instance + */ + public function parse(\Twig_Token $token) + { + $lineno = $token->getLine(); + $stream = $this->parser->getStream(); + + $stream->expect(\Twig_Token::BLOCK_END_TYPE); + $try = $this->parser->subparse([$this, 'decideCatch']); + $stream->next(); + $stream->expect(\Twig_Token::BLOCK_END_TYPE); + $catch = $this->parser->subparse([$this, 'decideEnd']); + $stream->next(); + $stream->expect(\Twig_Token::BLOCK_END_TYPE); + + return new TwigNodeTryCatch($try, $catch, $lineno, $this->getTag()); + } + + public function decideCatch(\Twig_Token $token) + { + return $token->test(array('catch')); + } + + public function decideEnd(\Twig_Token $token) + { + return $token->test(array('endtry')) || $token->test(array('endcatch')); + } + + /** + * Gets the tag name associated with this token parser. + * + * @return string The tag name + */ + public function getTag() + { + return 'try'; + } +} diff --git a/system/src/Grav/Common/Twig/Twig.php b/system/src/Grav/Common/Twig/Twig.php index dd7241147..e1ba281f4 100644 --- a/system/src/Grav/Common/Twig/Twig.php +++ b/system/src/Grav/Common/Twig/Twig.php @@ -2,7 +2,7 @@ /** * @package Grav.Common.Twig * - * @copyright Copyright (C) 2014 - 2017 RocketTheme, LLC. All rights reserved. + * @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Twig/TwigEnvironment.php b/system/src/Grav/Common/Twig/TwigEnvironment.php index ecea507e1..66ca8bf71 100644 --- a/system/src/Grav/Common/Twig/TwigEnvironment.php +++ b/system/src/Grav/Common/Twig/TwigEnvironment.php @@ -2,7 +2,7 @@ /** * @package Grav.Common.Twig * - * @copyright Copyright (C) 2014 - 2017 RocketTheme, LLC. All rights reserved. + * @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Twig/TwigExtension.php b/system/src/Grav/Common/Twig/TwigExtension.php index 2686f2057..9076cde1a 100644 --- a/system/src/Grav/Common/Twig/TwigExtension.php +++ b/system/src/Grav/Common/Twig/TwigExtension.php @@ -2,7 +2,7 @@ /** * @package Grav.Common.Twig * - * @copyright Copyright (C) 2014 - 2017 RocketTheme, LLC. All rights reserved. + * @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -11,14 +11,19 @@ use Grav\Common\Grav; use Grav\Common\Page\Collection; use Grav\Common\Page\Media; +use Grav\Common\Twig\TokenParser\TwigTokenParserScript; +use Grav\Common\Twig\TokenParser\TwigTokenParserStyle; +use Grav\Common\Twig\TokenParser\TwigTokenParserSwitch; +use Grav\Common\Twig\TokenParser\TwigTokenParserTryCatch; +use Grav\Common\Twig\TokenParser\TwigTokenParserMarkdown; use Grav\Common\Utils; use Grav\Common\Markdown\Parsedown; use Grav\Common\Markdown\ParsedownExtra; -use Grav\Common\Uri; use Grav\Common\Helpers\Base32; use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator; +use Symfony\Component\Yaml\Yaml; -class TwigExtension extends \Twig_Extension +class TwigExtension extends \Twig_Extension implements \Twig_Extension_GlobalsInterface { protected $grav; protected $debugger; @@ -34,16 +39,6 @@ public function __construct() $this->config = $this->grav['config']; } - /** - * Returns extension name. - * - * @return string - */ - public function getName() - { - return 'GravTwigExtension'; - } - /** * Register some standard globals * @@ -69,12 +64,13 @@ public function getFilters() new \Twig_SimpleFilter('contains', [$this, 'containsFilter']), new \Twig_SimpleFilter('chunk_split', [$this, 'chunkSplitFilter']), + new \Twig_SimpleFilter('nicenumber', [$this, 'niceNumberFunc']), new \Twig_SimpleFilter('defined', [$this, 'definedDefaultFilter']), new \Twig_SimpleFilter('ends_with', [$this, 'endsWithFilter']), new \Twig_SimpleFilter('fieldName', [$this, 'fieldNameFilter']), new \Twig_SimpleFilter('ksort', [$this, 'ksortFilter']), new \Twig_SimpleFilter('ltrim', [$this, 'ltrimFilter']), - new \Twig_SimpleFilter('markdown', [$this, 'markdownFilter']), + new \Twig_SimpleFilter('markdown', [$this, 'markdownFunction']), new \Twig_SimpleFilter('md5', [$this, 'md5Filter']), new \Twig_SimpleFilter('base32_encode', [$this, 'base32EncodeFilter']), new \Twig_SimpleFilter('base32_decode', [$this, 'base32DecodeFilter']), @@ -101,6 +97,8 @@ public function getFilters() new \Twig_SimpleFilter('basename', 'basename'), new \Twig_SimpleFilter('dirname', 'dirname'), new \Twig_SimpleFilter('print_r', 'print_r'), + new \Twig_SimpleFilter('yaml_encode', [$this, 'yamlEncodeFilter']), + new \Twig_SimpleFilter('yaml_decode', [$this, 'yamlDecodeFilter']), ]; } @@ -143,10 +141,28 @@ public function getFunctions() new \Twig_SimpleFunction('isajaxrequest', [$this, 'isAjaxFunc']), new \Twig_SimpleFunction('exif', [$this, 'exifFunc']), new \Twig_SimpleFunction('media_directory', [$this, 'mediaDirFunc']), + new \Twig_SimpleFunction('body_class', [$this, 'bodyClassFunc']), + new \Twig_SimpleFunction('theme_var', [$this, 'themeVarFunc']), + new \Twig_SimpleFunction('header_var', [$this, 'pageHeaderVarFunc']), + new \Twig_SimpleFunction('read_file', [$this, 'readFileFunc']), ]; } + /** + * @return array + */ + public function getTokenParsers() + { + return [ + new TwigTokenParserTryCatch(), + new TwigTokenParserScript(), + new TwigTokenParserStyle(), + new TwigTokenParserMarkdown(), + new TwigTokenParserSwitch(), + ]; + } + /** * Filters field name by changing dot notation into array notation. * @@ -172,10 +188,10 @@ public function safeEmailFilter($str) { $email = ''; for ( $i = 0, $len = strlen( $str ); $i < $len; $i++ ) { - $j = rand( 0, 1); - if ( $j == 0 ) { + $j = mt_rand( 0, 1); + if ( $j === 0 ) { $email .= '&#' . ord( $str[$i] ) . ';'; - } elseif ( $j == 1 ) { + } elseif ( $j === 1 ) { $email .= $str[$i]; } } @@ -205,7 +221,7 @@ public function randomizeFilter($original, $offset = 0) $random = array_slice($original, $offset); shuffle($random); - $sizeOf = sizeof($original); + $sizeOf = count($original); for ($x = 0; $x < $sizeOf; $x++) { if ($x < $offset) { $sorted[] = $original[$x]; @@ -220,9 +236,9 @@ public function randomizeFilter($original, $offset = 0) /** * Returns the modulus of an integer * - * @param int $number - * @param int $divider - * @param array $items array of items to select from to return + * @param string|int $number + * @param int $divider + * @param array $items array of items to select from to return * * @return int */ @@ -237,10 +253,10 @@ public function modulusFilter($number, $divider, $items = null) if (is_array($items)) { if (isset($items[$remainder])) { return $items[$remainder]; - } else { + } + return $items[0]; } - } return $remainder; } @@ -270,20 +286,23 @@ public function inflectorFilter($action, $data, $count = null) $inflector = $this->grav['inflector']; - if (in_array( + if (\in_array( $action, - ['titleize', 'camelize', 'underscorize', 'hyphenize', 'humanize', 'ordinalize', 'monthize'] + ['titleize', 'camelize', 'underscorize', 'hyphenize', 'humanize', 'ordinalize', 'monthize'], + true )) { return $inflector->$action($data); - } elseif (in_array($action, ['pluralize', 'singularize'])) { + } + + if (\in_array($action, ['pluralize', 'singularize'], true)) { if ($count) { return $inflector->$action($data, $count); - } else { + } + return $inflector->$action($data); } - } else { + return $data; - } } /** @@ -350,13 +369,13 @@ public function base64DecodeFilter($str) * @param string $filter * @param array|int $direction * - * @return string + * @return array */ - public function sortByKeyFilter(array $input, $filter, $direction = SORT_ASC) + public function sortByKeyFilter($input, $filter, $direction = SORT_ASC) { $output = []; - if (!$input) { + if (!is_array($input) || !$input) { return $output; } @@ -378,7 +397,7 @@ public function sortByKeyFilter(array $input, $filter, $direction = SORT_ASC) */ public function ksortFilter($array) { - if (is_null($array)) { + if (null === $array) { $array = []; } ksort($array); @@ -503,9 +522,8 @@ public function nicetimeFilter($date, $long_strings = true) if ($now == $unix_date) { return "{$tense}"; } - else { + return "$difference $periods[$j] {$tense}"; - } } /** @@ -528,7 +546,7 @@ public function absoluteUrlFilter($string) * @param bool $block Block or Line processing * @return mixed|string */ - public function markdownFilter($string, $block = true) + public function markdownFunction($string, $block = true) { $page = $this->grav['page']; $defaults = $this->config->get('system.pages.markdown'); @@ -580,12 +598,8 @@ public function endsWithFilter($haystack, $needle) */ public function definedDefaultFilter($value, $default = null) { - if (isset($value)) { - return $value; - } else { - return $default; + return null !== $value ? $value : $default; } - } /** * @param $value @@ -668,38 +682,9 @@ public function repeatFunc($input, $multiplier) */ public function urlFunc($input, $domain = false) { - if (!trim((string)$input)) { - return false; - } - - if ($this->grav['config']->get('system.absolute_urls', false)) { - $domain = true; + return Utils::url($input, $domain); } - if (Grav::instance()['uri']->isExternal($input)) { - return $input; - } - - $input = ltrim((string)$input, '/'); - - if (Utils::contains((string)$input, '://')) { - /** @var UniformResourceLocator $locator */ - $locator = $this->grav['locator']; - - - - // Get relative path to the resource (or false if not found). - $resource = $locator->findResource($input, false); - } else { - $resource = $input; - } - - /** @var Uri $uri */ - $uri = $this->grav['uri']; - - return $resource ? rtrim($uri->rootUrl($domain), '/') . '/' . $resource : null; - } - /** * This function will evaluate Twig $twig through the $environment, and return its results. * @@ -726,8 +711,7 @@ public function evaluateTwigFunc($context, $twig ) { */ public function evaluateStringFunc($context, $string ) { - $parsed = $this->evaluateTwigFunc($context, "{{ $string }}"); - return $parsed; + return $this->evaluateTwigFunc($context, "{{ $string }}"); } @@ -829,7 +813,7 @@ public function arrayFunc($value) * * @param string $key key of item * @param string $val value of item - * @param string $current_array optional array to add to + * @param array $current_array optional array to add to * * @return array */ @@ -837,11 +821,11 @@ public function arrayKeyValueFunc($key, $val, $current_array = null) { if (empty($current_array)) { return array($key => $val); - } else { + } + $current_array[$key] = $val; return $current_array; } - } /** * Wrapper for array_intersect() method @@ -854,10 +838,9 @@ public function arrayIntersectFunc($array1, $array2) { if ($array1 instanceof Collection && $array2 instanceof Collection) { return $array1->intersect($array2); - } else { - return array_intersect($array1, $array2); } + return array_intersect($array1, $array2); } /** @@ -871,10 +854,10 @@ public function stringFunc($value) { if (is_array($value)) { //format the array as a string return json_encode($value); - } else { + } + return $value; } - } /** * Translate a string @@ -907,8 +890,8 @@ public function authorize($action) foreach ($action as $key => $perms) { $prefix = is_int($key) ? '' : $key . '.'; $perms = $prefix ? (array) $perms : [$perms => true]; - foreach ($perms as $action => $authenticated) { - if ($this->grav['user']->authorize($prefix . $action)) { + foreach ($perms as $action2 => $authenticated) { + if ($this->grav['user']->authorize($prefix . $action2)) { return $authenticated; } } @@ -1011,7 +994,7 @@ public function isAjaxFunc() { return ( !empty($_SERVER['HTTP_X_REQUESTED_WITH']) - && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest'); + && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) === 'xmlhttprequest'); } /** @@ -1041,19 +1024,43 @@ public function exifFunc($image, $raw = false) if ($exif_data) { if ($raw) { return $exif_data->getRawData(); - } else { + } + return $exif_data->getData(); } } } + + return null; + } + + /** + * Simple function to read a file based on a filepath and output it + * + * @param $filepath + * @return bool|string + */ + public function readFileFunc($filepath) + { + /** @var UniformResourceLocator $locator */ + $locator = $this->grav['locator']; + + if ($locator->isStream($filepath)) { + $filepath = $locator->findResource($filepath); } + + if (file_exists($filepath)) { + return file_get_contents($filepath); + } + + return false; } /** * Process a folder as Media and return a media object * * @param $media_dir - * @return Media + * @return Media|null */ public function mediaDirFunc($media_dir) { @@ -1068,6 +1075,7 @@ public function mediaDirFunc($media_dir) return new Media($media_dir); } + return null; } /** @@ -1080,4 +1088,132 @@ public function vardumpFunc($var) var_dump($var); } + /** + * Returns a nicer more readable number + * + * @param int|float $n + * @return bool|string + */ + public function niceNumberFunc($n) + { + // first strip any formatting; + $n = 0 + str_replace(',', '', $n); + + // is this a number? + if (!is_numeric($n)) { + return false; + } + + // now filter it; + if ($n > 1000000000000) { + return round(($n/1000000000000), 2).' t'; + } + if ($n > 1000000000) { + return round(($n/1000000000), 2).' b'; + } + if ($n > 1000000) { + return round(($n/1000000), 2).' m'; + } + if ($n > 1000) { + return round(($n/1000), 2).' k'; + } + + return number_format($n); + } + + /** + * Get a theme variable + * + * @param $var + * @param bool $default + * @return string + */ + public function themeVarFunc($var, $default = null) + { + $header = $this->grav['page']->header(); + $header_classes = isset($header->$var) ? $header->$var : null; + return $header_classes ?: $this->config->get('theme.' . $var, $default); + } + + /** + * takes an array of classes, and if they are not set on body_classes + * look to see if they are set in theme config + * + * @param $classes + * @return string + */ + public function bodyClassFunc($classes) + { + + $header = $this->grav['page']->header(); + $body_classes = isset($header->body_classes) ? $header->body_classes : ''; + + foreach ((array)$classes as $class) { + if (!empty($body_classes) && Utils::contains($body_classes, $class)) { + continue; + } + + $val = $this->config->get('theme.' . $class, false) ? $class : false; + $body_classes .= $val ? ' ' . $val : ''; + } + + return $body_classes; + } + + /** + * Look for a page header variable in an array of pages working its way through until a value is found + * + * @param $var + * @param null $pages + * @return mixed + */ + public function pageHeaderVarFunc($var, $pages = null) + { + if ($pages === null) { + $pages = $this->grav['page']; + } + + // Make sure pages are an array + if (!is_array($pages)) { + $pages = array($pages); + } + + // Loop over pages and look for header vars + foreach ($pages as $page) { + if (is_string($page)) { + $page = $this->grav['pages']->find($page); + } + + if ($page) { + $header = $page->header(); + if (isset($header->$var)) { + return $header->$var; + } + } + } + + return null; + } + + /** + * Dump/Encode data into YAML format + * + * @param $data + * @return mixed + */ + public function yamlEncodeFilter($data) + { + return Yaml::dump($data, 10); + } + + /** + * Decode/Parse data from YAML format + * + * @param $data + * @return mixed + */ + public function yamlDecodeFilter($data) + { + return Yaml::parse($data); + } } diff --git a/system/src/Grav/Common/Twig/WriteCacheFileTrait.php b/system/src/Grav/Common/Twig/WriteCacheFileTrait.php index 59275456a..c413ca6fb 100644 --- a/system/src/Grav/Common/Twig/WriteCacheFileTrait.php +++ b/system/src/Grav/Common/Twig/WriteCacheFileTrait.php @@ -2,7 +2,7 @@ /** * @package Grav.Common.Twig * - * @copyright Copyright (C) 2014 - 2017 RocketTheme, LLC. All rights reserved. + * @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Uri.php b/system/src/Grav/Common/Uri.php index d068a045f..75fcf5d2a 100644 --- a/system/src/Grav/Common/Uri.php +++ b/system/src/Grav/Common/Uri.php @@ -2,18 +2,29 @@ /** * @package Grav.Common * - * @copyright Copyright (C) 2014 - 2017 RocketTheme, LLC. All rights reserved. + * @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ namespace Grav\Common; +use Grav\Common\Config\Config; +use Grav\Common\Language\Language; use Grav\Common\Page\Page; +use Grav\Framework\Route\RouteFactory; +use Grav\Framework\Uri\UriFactory; +use Grav\Framework\Uri\UriPartsFilter; class Uri { const HOSTNAME_REGEX = '/^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])$/'; + /** @var \Grav\Framework\Uri\Uri */ + protected static $currentUri; + + /** @var \Grav\Framework\Route\Route */ + protected static $currentRoute; + public $url; // Uri parts. @@ -52,126 +63,6 @@ public function __construct($env = null) } } - /** - * @param array $env - */ - protected function createFromEnvironment(array $env) - { - // Build scheme. - if (isset($env['HTTP_X_FORWARDED_PROTO'])) { - $this->scheme = $env['HTTP_X_FORWARDED_PROTO']; - } elseif (isset($env['X-FORWARDED-PROTO'])) { - $this->scheme = $env['X-FORWARDED-PROTO']; - } elseif (isset($env['REQUEST_SCHEME'])) { - $this->scheme = $env['REQUEST_SCHEME']; - } else { - $https = isset($env['HTTPS']) ? $env['HTTPS'] : ''; - $this->scheme = (empty($https) || strtolower($https) === 'off') ? 'http' : 'https'; - } - - // Build user and password. - $this->user = isset($env['PHP_AUTH_USER']) ? $env['PHP_AUTH_USER'] : null; - $this->password = isset($env['PHP_AUTH_PW']) ? $env['PHP_AUTH_PW'] : null; - - // Build host. - $hostname = 'localhost'; - if (isset($env['HTTP_HOST'])) { - $hostname = $env['HTTP_HOST']; - } elseif (isset($env['SERVER_NAME'])) { - $hostname = $env['SERVER_NAME']; - } - // Remove port from HTTP_HOST generated $hostname - $hostname = Utils::substrToString($hostname, ':'); - // Validate the hostname - $this->host = $this->validateHostname($hostname) ? $hostname : 'unknown'; - - // Build port. - if (isset($env['HTTP_X_FORWARDED_PORT'])) { - $this->port = (int)$env['HTTP_X_FORWARDED_PORT']; - } elseif (isset($env['X-FORWARDED-PORT'])) { - $this->port = (int)$env['X-FORWARDED-PORT']; - } elseif (isset($env['SERVER_PORT'])) { - $this->port = (int)$env['SERVER_PORT']; - } else { - $this->port = null; - } - if ($this->hasStandardPort()) { - $this->port = null; - } - - // Build path. - $request_uri = isset($env['REQUEST_URI']) ? $env['REQUEST_URI'] : ''; - $this->path = rawurldecode(parse_url('http://example.com' . $request_uri, PHP_URL_PATH)); - - // Build query string. - $this->query = isset($env['QUERY_STRING']) ? $env['QUERY_STRING'] : ''; - if ($this->query === '') { - $this->query = parse_url('http://example.com' . $request_uri, PHP_URL_QUERY); - } - - // Support ngnix routes. - if (strpos($this->query, '_url=') === 0) { - parse_str($this->query, $query); - unset($query['_url']); - $this->query = http_build_query($query); - } - - // Build fragment. - $this->fragment = null; - - // Filter userinfo, path and query string. - $this->user = $this->user !== null ? static::filterUserInfo($this->user) : null; - $this->password = $this->password !== null ? static::filterUserInfo($this->password) : null; - $this->path = empty($this->path) ? '/' : static::filterPath($this->path); - $this->query = static::filterQuery($this->query); - - $this->reset(); - } - - /** - * Does this Uri use a standard port? - * - * @return bool - */ - protected function hasStandardPort() - { - return ($this->scheme === 'http' && $this->port === 80) || ($this->scheme === 'https' && $this->port === 443); - } - - /** - * @param string $url - */ - protected function createFromString($url) - { - // Set Uri parts. - $parts = parse_url($url); - if ($parts === false) { - throw new \RuntimeException('Malformed URL: ' . $url); - } - $this->scheme = isset($parts['scheme']) ? $parts['scheme'] : null; - $this->user = isset($parts['user']) ? $parts['user'] : null; - $this->password = isset($parts['pass']) ? $parts['pass'] : null; - $this->host = isset($parts['host']) ? $parts['host'] : null; - $this->port = isset($parts['port']) ? (int)$parts['port'] : null; - $this->path = isset($parts['path']) ? $parts['path'] : ''; - $this->query = isset($parts['query']) ? $parts['query'] : ''; - $this->fragment = isset($parts['fragment']) ? $parts['fragment'] : null; - - // Validate the hostname - if ($this->host) { - $this->host = $this->validateHostname($this->host) ? $this->host : 'unknown'; - } - - // Filter userinfo, path, query string and fragment. - $this->user = $this->user !== null ? static::filterUserInfo($this->user) : null; - $this->password = $this->password !== null ? static::filterUserInfo($this->password) : null; - $this->path = empty($this->path) ? '/' : static::filterPath($this->path); - $this->query = static::filterQuery($this->query); - $this->fragment = $this->fragment !== null ? static::filterQuery($this->fragment) : null; - - $this->reset(); - } - /** * Initialize the URI class with a url passed via parameter. * Used for testing purposes. @@ -205,33 +96,6 @@ public function initializeWithUrlAndRootPath($url, $root_path) return $this; } - protected function reset() - { - // resets - parse_str($this->query, $this->queries); - $this->extension = null; - $this->basename = null; - $this->paths = []; - $this->params = []; - $this->env = $this->buildEnvironment(); - $this->uri = $this->path . (!empty($this->query) ? '?' . $this->query : ''); - - $this->base = $this->buildBaseUrl(); - $this->root_path = $this->buildRootPath(); - $this->root = $this->base . $this->root_path; - $this->url = $this->base . $this->uri; - } - - /** - * Calculate the parameter regex based on the param_sep setting - * - * @return string - */ - public static function paramsRegex() - { - return '/\/([^\:\#\/\?]*' . Grav::instance()['config']->get('system.param_sep') . '[^\:\#\/\?]*)/'; - } - /** * Validate a hostname * @@ -244,45 +108,6 @@ public function validateHostname($hostname) return (bool)preg_match(static::HOSTNAME_REGEX, $hostname); } - /** - * Get the base URI with port if needed - * - * @return string - */ - private function buildBaseUrl() - { - return $this->scheme() . $this->host; - } - - /** - * Get the Grav Root Path - * - * @return string - */ - private function buildRootPath() - { - // In Windows script path uses backslash, convert it: - $scriptPath = str_replace('\\', '/', $_SERVER['PHP_SELF']); - $rootPath = str_replace(' ', '%20', rtrim(substr($scriptPath, 0, strpos($scriptPath, 'index.php')), '/')); - - // check if userdir in the path and workaround PHP bug with PHP_SELF - if (strpos($this->uri, '/~') !== false && strpos($scriptPath, '/~') === false) { - $rootPath = substr($this->uri, 0, strpos($this->uri, '/', 1)) . $rootPath; - } - - return $rootPath; - } - - private function buildEnvironment() - { - // check for localhost variations - if ($this->host === '127.0.0.1' || $this->host === '::1') { - return 'localhost'; - } - - return $this->host ?: 'unknown'; - } - /** * Initializes the URI object based on the url set on the object */ @@ -290,7 +115,10 @@ public function init() { $grav = Grav::instance(); + /** @var Config $config */ $config = $grav['config']; + + /** @var Language $language */ $language = $grav['language']; // add the port to the base for non-standard ports @@ -313,7 +141,7 @@ public function init() $this->url = $this->base . $this->uri; - $uri = str_replace($this->root, '', $this->url); + $uri = str_replace(static::filterPath($this->root), '', $this->url); // remove the setup.php based base if set: $setup_base = $grav['pages']->base(); @@ -374,31 +202,9 @@ public function init() $grav['base_url_absolute'] = $grav['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']; - } - /** - * Process any params based in this URL, supports any valid delimiter - * - * @param $uri - * @param string $delimiter - * - * @return string - */ - private function processParams($uri, $delimiter = ':') - { - if (strpos($uri, $delimiter) !== false) { - preg_match_all(static::paramsRegex(), $uri, $matches, PREG_SET_ORDER); - - foreach ($matches as $match) { - $param = explode($delimiter, $match[1]); - if (count($param) === 2) { - $plain_var = filter_var($param[1], FILTER_SANITIZE_STRING); - $this->params[$param[0]] = $plain_var; - $uri = str_replace($match[0], '', $uri); - } - } - } - return $uri; + RouteFactory::setRoot($this->root_path); + RouteFactory::setLanguage($language->getLanguageURLPrefix()); } /** @@ -749,6 +555,36 @@ public function referrer($default = null, $attributes = null) return substr($referrer, strlen($root)); } + public function __toString() + { + return static::buildUrl($this->toArray()); + } + + public function toArray() + { + return [ + 'scheme' => $this->scheme, + 'host' => $this->host, + 'port' => $this->port, + 'user' => $this->user, + 'pass' => $this->password, + 'path' => $this->path, + 'params' => $this->params, + 'query' => $this->query, + 'fragment' => $this->fragment + ]; + } + + /** + * Calculate the parameter regex based on the param_sep setting + * + * @return string + */ + public static function paramsRegex() + { + return '/\/([^\:\#\/\?]*' . Grav::instance()['config']->get('system.param_sep') . '[^\:\#\/\?]*)/'; + } + /** * Return the IP address of the current user * @@ -775,6 +611,35 @@ public static function ip() return $ip; } + /** + + * Returns current Uri. + * + * @return \Grav\Framework\Uri\Uri + */ + public static function getCurrentUri() + { + if (!static::$currentUri) { + static::$currentUri = UriFactory::createFromEnvironment($_SERVER); + } + + return static::$currentUri; + } + + /** + * Returns current route. + * + * @return \Grav\Framework\Route\Route + */ + public static function getCurrentRoute() + { + if (!static::$currentRoute) { + $uri = Grav::instance()['uri']; + static::$currentRoute = RouteFactory::createFromParts($uri->toArray()); + } + + return static::$currentRoute; + } /** * Is this an external URL? if it starts with `http` then yes, else false @@ -788,26 +653,6 @@ public static function isExternal($url) return Utils::startsWith($url, 'http'); } - public function __toString() - { - return static::buildUrl($this->toArray()); - } - - public function toArray() - { - return [ - 'scheme' => $this->scheme, - 'host' => $this->host, - 'port' => $this->port, - 'user' => $this->user, - 'pass' => $this->password, - 'path' => $this->path, - 'params' => $this->params, - 'query' => $this->query, - 'fragment' => $this->fragment - ]; - } - /** * The opposite of built-in PHP method parse_url() * @@ -997,7 +842,7 @@ public static function convertUrl(Page $page, $url, $type = 'link', $absolute = // Handle route only if ($route_only) { - $url_path = str_replace($base_url, '', $url_path); + $url_path = str_replace(static::filterPath($base_url), '', $url_path); } // transform back to string/array as needed @@ -1212,13 +1057,7 @@ public static function cleanPath($path) */ public static function filterUserInfo($info) { - return preg_replace_callback( - '/(?:[^a-zA-Z0-9_\-\.~!\$&\'\(\)\*\+,;=]+|%(?![A-Fa-f0-9]{2}))/u', - function ($match) { - return rawurlencode($match[0]); - }, - $info - ); + return $info !== null ? UriPartsFilter::filterUserInfo($info) : ''; } /** @@ -1235,13 +1074,7 @@ function ($match) { */ public static function filterPath($path) { - return preg_replace_callback( - '/(?:[^a-zA-Z0-9_\-\.~:@&=\+\$,\/;%]+|%(?![A-Fa-f0-9]{2}))/u', - function ($match) { - return rawurlencode($match[0]); - }, - $path - ); + return $path !== null ? UriPartsFilter::filterPath($path) : ''; } /** @@ -1252,12 +1085,195 @@ function ($match) { */ public static function filterQuery($query) { - return preg_replace_callback( - '/(?:[^a-zA-Z0-9_\-\.~!\$&\'\(\)\*\+,;=%:@\/\?]+|%(?![A-Fa-f0-9]{2}))/u', - function ($match) { - return rawurlencode($match[0]); - }, - $query - ); + return $query !== null ? UriPartsFilter::filterQueryOrFragment($query) : ''; + } + + /** + * @param array $env + */ + protected function createFromEnvironment(array $env) + { + // Build scheme. + if (isset($env['REQUEST_SCHEME'])) { + $this->scheme = $env['REQUEST_SCHEME']; + } else { + $https = isset($env['HTTPS']) ? $env['HTTPS'] : ''; + $this->scheme = (empty($https) || strtolower($https) === 'off') ? 'http' : 'https'; + } + + // Build user and password. + $this->user = isset($env['PHP_AUTH_USER']) ? $env['PHP_AUTH_USER'] : null; + $this->password = isset($env['PHP_AUTH_PW']) ? $env['PHP_AUTH_PW'] : null; + + // Build host. + $hostname = 'localhost'; + if (isset($env['HTTP_HOST'])) { + $hostname = $env['HTTP_HOST']; + } elseif (isset($env['SERVER_NAME'])) { + $hostname = $env['SERVER_NAME']; + } + // Remove port from HTTP_HOST generated $hostname + $hostname = Utils::substrToString($hostname, ':'); + // Validate the hostname + $this->host = $this->validateHostname($hostname) ? $hostname : 'unknown'; + + // Build port. + $this->port = isset($env['SERVER_PORT']) ? (int)$env['SERVER_PORT'] : null; + if ($this->hasStandardPort()) { + $this->port = null; + } + + // Build path. + $request_uri = isset($env['REQUEST_URI']) ? $env['REQUEST_URI'] : ''; + $this->path = rawurldecode(parse_url('http://example.com' . $request_uri, PHP_URL_PATH)); + + // Build query string. + $this->query = isset($env['QUERY_STRING']) ? $env['QUERY_STRING'] : ''; + if ($this->query === '') { + $this->query = parse_url('http://example.com' . $request_uri, PHP_URL_QUERY); + } + + // Support ngnix routes. + if (strpos($this->query, '_url=') === 0) { + parse_str($this->query, $query); + unset($query['_url']); + $this->query = http_build_query($query); + } + + // Build fragment. + $this->fragment = null; + + // Filter userinfo, path and query string. + $this->user = $this->user !== null ? static::filterUserInfo($this->user) : null; + $this->password = $this->password !== null ? static::filterUserInfo($this->password) : null; + $this->path = empty($this->path) ? '/' : static::filterPath($this->path); + $this->query = static::filterQuery($this->query); + + $this->reset(); + } + + /** + * Does this Uri use a standard port? + * + * @return bool + */ + protected function hasStandardPort() + { + return ($this->scheme === 'http' && $this->port === 80) || ($this->scheme === 'https' && $this->port === 443); + } + + /** + * @param string $url + */ + protected function createFromString($url) + { + // Set Uri parts. + $parts = parse_url($url); + if ($parts === false) { + throw new \RuntimeException('Malformed URL: ' . $url); + } + $this->scheme = isset($parts['scheme']) ? $parts['scheme'] : null; + $this->user = isset($parts['user']) ? $parts['user'] : null; + $this->password = isset($parts['pass']) ? $parts['pass'] : null; + $this->host = isset($parts['host']) ? $parts['host'] : null; + $this->port = isset($parts['port']) ? (int)$parts['port'] : null; + $this->path = isset($parts['path']) ? $parts['path'] : ''; + $this->query = isset($parts['query']) ? $parts['query'] : ''; + $this->fragment = isset($parts['fragment']) ? $parts['fragment'] : null; + + // Validate the hostname + if ($this->host) { + $this->host = $this->validateHostname($this->host) ? $this->host : 'unknown'; + } + + // Filter userinfo, path, query string and fragment. + $this->user = $this->user !== null ? static::filterUserInfo($this->user) : null; + $this->password = $this->password !== null ? static::filterUserInfo($this->password) : null; + $this->path = empty($this->path) ? '/' : static::filterPath($this->path); + $this->query = static::filterQuery($this->query); + $this->fragment = $this->fragment !== null ? static::filterQuery($this->fragment) : null; + + $this->reset(); + } + + protected function reset() + { + // resets + parse_str($this->query, $this->queries); + $this->extension = null; + $this->basename = null; + $this->paths = []; + $this->params = []; + $this->env = $this->buildEnvironment(); + $this->uri = $this->path . (!empty($this->query) ? '?' . $this->query : ''); + + $this->base = $this->buildBaseUrl(); + $this->root_path = $this->buildRootPath(); + $this->root = $this->base . $this->root_path; + $this->url = $this->base . $this->uri; + } + + /** + * Get the base URI with port if needed + * + * @return string + */ + private function buildBaseUrl() + { + return $this->scheme() . $this->host; + } + + /** + * Get the Grav Root Path + * + * @return string + */ + private function buildRootPath() + { + // In Windows script path uses backslash, convert it: + $scriptPath = str_replace('\\', '/', $_SERVER['PHP_SELF']); + $rootPath = str_replace(' ', '%20', rtrim(substr($scriptPath, 0, strpos($scriptPath, 'index.php')), '/')); + + // check if userdir in the path and workaround PHP bug with PHP_SELF + if (strpos($this->uri, '/~') !== false && strpos($scriptPath, '/~') === false) { + $rootPath = substr($this->uri, 0, strpos($this->uri, '/', 1)) . $rootPath; + } + + return $rootPath; + } + + private function buildEnvironment() + { + // check for localhost variations + if ($this->host === '127.0.0.1' || $this->host === '::1') { + return 'localhost'; + } + + return $this->host ?: 'unknown'; + } + + /** + * Process any params based in this URL, supports any valid delimiter + * + * @param $uri + * @param string $delimiter + * + * @return string + */ + private function processParams($uri, $delimiter = ':') + { + if (strpos($uri, $delimiter) !== false) { + preg_match_all(static::paramsRegex(), $uri, $matches, PREG_SET_ORDER); + + foreach ($matches as $match) { + $param = explode($delimiter, $match[1]); + if (count($param) === 2) { + $plain_var = filter_var($param[1], FILTER_SANITIZE_STRING); + $this->params[$param[0]] = $plain_var; + $uri = str_replace($match[0], '', $uri); + } + } + } + return $uri; } } diff --git a/system/src/Grav/Common/User/Authentication.php b/system/src/Grav/Common/User/Authentication.php index a3e12ff7e..b46750c29 100644 --- a/system/src/Grav/Common/User/Authentication.php +++ b/system/src/Grav/Common/User/Authentication.php @@ -2,7 +2,7 @@ /** * @package Grav.Common.User * - * @copyright Copyright (C) 2014 - 2017 RocketTheme, LLC. All rights reserved. + * @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/User/Group.php b/system/src/Grav/Common/User/Group.php index f8bf2aca9..a251326a4 100644 --- a/system/src/Grav/Common/User/Group.php +++ b/system/src/Grav/Common/User/Group.php @@ -2,12 +2,13 @@ /** * @package Grav.Common.User * - * @copyright Copyright (C) 2014 - 2017 RocketTheme, LLC. All rights reserved. + * @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ namespace Grav\Common\User; +use Grav\Common\Config\Config; use Grav\Common\Data\Blueprints; use Grav\Common\Data\Data; use Grav\Common\File\CompiledYamlFile; @@ -23,9 +24,7 @@ class Group extends Data */ private static function groups() { - $groups = Grav::instance()['config']->get('groups'); - - return $groups; + return Grav::instance()['config']->get('groups', []); } /** @@ -37,7 +36,7 @@ public static function groupNames() { $groups = []; - foreach(Grav::instance()['config']->get('groups', []) as $groupname => $group) { + foreach(static::groups() as $groupname => $group) { $groups[$groupname] = isset($group['readableName']) ? $group['readableName'] : $groupname; } @@ -65,20 +64,15 @@ public static function groupExists($groupname) */ public static function load($groupname) { - if (self::groupExists($groupname)) { - $content = self::groups()[$groupname]; - } else { - $content = []; - } + $groups = self::groups(); + + $content = isset($groups[$groupname]) ? $groups[$groupname] : []; + $content += ['groupname' => $groupname]; $blueprints = new Blueprints; $blueprint = $blueprints->get('user/group'); - if (!isset($content['groupname'])) { - $content['groupname'] = $groupname; - } - $group = new Group($content, $blueprint); - return $group; + return new Group($content, $blueprint); } /** @@ -87,29 +81,30 @@ public static function load($groupname) public function save() { $grav = Grav::instance(); + + /** @var Config $config */ $config = $grav['config']; $blueprints = new Blueprints; $blueprint = $blueprints->get('user/group'); - $fields = $blueprint->fields(); - - $config->set("groups.$this->groupname", []); + $config->set("groups.{$this->groupname}", []); + $fields = $blueprint->fields(); foreach ($fields as $field) { - if ($field['type'] == 'text') { + if ($field['type'] === 'text') { $value = $field['name']; if (isset($this->items['data'][$value])) { - $config->set("groups.$this->groupname.$value", $this->items['data'][$value]); + $config->set("groups.{$this->groupname}.{$value}", $this->items['data'][$value]); } } - if ($field['type'] == 'array' || $field['type'] == 'permissions') { + if ($field['type'] === 'array' || $field['type'] === 'permissions') { $value = $field['name']; $arrayValues = Utils::getDotNotation($this->items['data'], $field['name']); if ($arrayValues) { foreach ($arrayValues as $arrayIndex => $arrayValue) { - $config->set("groups.$this->groupname.$value.$arrayIndex", $arrayValue); + $config->set("groups.{$this->groupname}.{$value}.{$arrayIndex}", $arrayValue); } } } @@ -117,9 +112,11 @@ public function save() $type = 'groups'; $blueprints = $this->blueprints("config/{$type}"); + + $filename = CompiledYamlFile::instance($grav['locator']->findResource("config://{$type}.yaml")); + $obj = new Data($config->get($type), $blueprints); - $file = CompiledYamlFile::instance($grav['locator']->findResource("config://{$type}.yaml")); - $obj->file($file); + $obj->file($filename); $obj->save(); } @@ -133,18 +130,23 @@ public function save() public static function remove($groupname) { $grav = Grav::instance(); + + /** @var Config $config */ $config = $grav['config']; + $blueprints = new Blueprints; $blueprint = $blueprints->get('user/group'); - $groups = $config->get("groups"); + $type = 'groups'; + + $groups = $config->get($type); unset($groups[$groupname]); - $config->set("groups", $groups); + $config->set($type, $groups); - $type = 'groups'; - $obj = new Data($config->get($type), $blueprint); - $file = CompiledYamlFile::instance($grav['locator']->findResource("config://{$type}.yaml")); - $obj->file($file); + $filename = CompiledYamlFile::instance($grav['locator']->findResource("config://{$type}.yaml")); + + $obj = new Data($groups, $blueprint); + $obj->file($filename); $obj->save(); return true; diff --git a/system/src/Grav/Common/User/User.php b/system/src/Grav/Common/User/User.php index 1cd345b03..c1c23464d 100644 --- a/system/src/Grav/Common/User/User.php +++ b/system/src/Grav/Common/User/User.php @@ -2,7 +2,7 @@ /** * @package Grav.Common.User * - * @copyright Copyright (C) 2014 - 2017 RocketTheme, LLC. All rights reserved. + * @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -13,6 +13,7 @@ use Grav\Common\File\CompiledYamlFile; use Grav\Common\Grav; use Grav\Common\Utils; +use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator; class User extends Data { @@ -22,12 +23,14 @@ class User extends Data * Always creates user object. To check if user exists, use $this->exists(). * * @param string $username + * @param bool $setConfig * * @return User */ public static function load($username) { $grav = Grav::instance(); + /** @var UniformResourceLocator $locator */ $locator = $grav['locator']; // force lowercase of username @@ -35,15 +38,11 @@ public static function load($username) $blueprints = new Blueprints; $blueprint = $blueprints->get('user/account'); + $file_path = $locator->findResource('account://' . $username . YAML_EXT); $file = CompiledYamlFile::instance($file_path); - $content = (array)$file->content(); - if (!isset($content['username'])) { - $content['username'] = $username; - } - if (!isset($content['state'])) { - $content['state'] = 'enabled'; - } + $content = (array)$file->content() + ['username' => $username, 'state' => 'enabled']; + $user = new User($content, $blueprint); $user->file($file); @@ -76,7 +75,7 @@ public static function find($query, $fields = ['username', 'email']) if (Utils::endsWith($file, YAML_EXT)) { $find_user = User::load(trim(pathinfo($file, PATHINFO_FILENAME))); foreach ($fields as $field) { - if ($find_user[$field] == $query) { + if ($find_user[$field] === $query) { return $find_user; } } @@ -128,20 +127,20 @@ public function authenticate($password) ); return false; - } else { - // Plain-text does match, we can update the hash and proceed - $save = true; - - $this->hashed_password = Authentication::create($this->password); - unset($this->password); } + // Plain-text does match, we can update the hash and proceed + $save = true; + + $this->hashed_password = Authentication::create($this->password); + unset($this->password); + } $result = Authentication::verify($password, $this->hashed_password); // Password needs to be updated, save the file. - if ($result == 2) { + if ($result === 2) { $save = true; $this->hashed_password = Authentication::create($password); } @@ -251,8 +250,8 @@ public function avatarUrl() $avatar = $this->avatar; $avatar = array_shift($avatar); return Grav::instance()['base_url'] . '/' . $avatar['path']; - } else { - return 'https://www.gravatar.com/avatar/' . md5($this->email); } + + return 'https://www.gravatar.com/avatar/' . md5($this->email); } } diff --git a/system/src/Grav/Common/Utils.php b/system/src/Grav/Common/Utils.php index b4743774e..9193a6708 100644 --- a/system/src/Grav/Common/Utils.php +++ b/system/src/Grav/Common/Utils.php @@ -2,7 +2,7 @@ /** * @package Grav.Common * - * @copyright Copyright (C) 2014 - 2017 RocketTheme, LLC. All rights reserved. + * @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -10,73 +10,117 @@ use DateTime; use Grav\Common\Helpers\Truncator; +use Grav\Common\Page\Page; use RocketTheme\Toolbox\Event\Event; +use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator; abstract class Utils { protected static $nonces = []; + /** + * Simple helper method to make getting a Grav URL easier + * + * @param $input + * @param bool $domain + * @return bool|null|string + */ + public static function url($input, $domain = false) + { + if (!trim((string)$input)) { + return false; + } + + if (Grav::instance()['config']->get('system.absolute_urls', false)) { + $domain = true; + } + + if (Grav::instance()['uri']->isExternal($input)) { + return $input; + } + + $input = ltrim((string)$input, '/'); + + if (Utils::contains((string)$input, '://')) { + /** @var UniformResourceLocator $locator */ + $locator = Grav::instance()['locator']; + + // Get relative path to the resource (or false if not found). + $resource = $locator->findResource($input, false); + } else { + $resource = $input; + } + + /** @var Uri $uri */ + $uri = Grav::instance()['uri']; + + return $resource ? rtrim($uri->rootUrl($domain), '/') . '/' . $resource : null; + } + /** * Check if the $haystack string starts with the substring $needle * * @param string $haystack - * @param string $needle + * @param string|string[] $needle * * @return bool */ public static function startsWith($haystack, $needle) { - if (is_array($needle)) { - $status = false; - foreach ($needle as $each_needle) { - $status = $status || ($each_needle === '' || strpos($haystack, $each_needle) === 0); - if ($status) { - return $status; - } - } + $status = false; - return $status; + foreach ((array)$needle as $each_needle) { + $status = $each_needle === '' || strpos($haystack, $each_needle) === 0; + if ($status) { + break; + } } - return $needle === '' || strpos($haystack, $needle) === 0; + return $status; } /** * Check if the $haystack string ends with the substring $needle * * @param string $haystack - * @param string $needle + * @param string|string[] $needle * * @return bool */ public static function endsWith($haystack, $needle) { - if (is_array($needle)) { - $status = false; - foreach ($needle as $each_needle) { - $status = $status || ($each_needle === '' || substr($haystack, -strlen($each_needle)) === $each_needle); - if ($status) { - return $status; - } - } + $status = false; - return $status; + foreach ((array)$needle as $each_needle) { + $status = $each_needle === '' || substr($haystack, -strlen($each_needle)) === $each_needle; + if ($status) { + break; + } } - return $needle === '' || substr($haystack, -strlen($needle)) === $needle; + return $status; } /** * Check if the $haystack string contains the substring $needle * * @param string $haystack - * @param string $needle + * @param string|string[] $needle * * @return bool */ public static function contains($haystack, $needle) { - return $needle === '' || strpos($haystack, $needle) !== false; + $status = false; + + foreach ((array)$needle as $each_needle) { + $status = $each_needle === '' || strpos($haystack, $each_needle) !== false; + if ($status) { + break; + } + } + + return $status; } /** @@ -158,14 +202,18 @@ public static function mergeObjects($obj1, $obj2) */ public static function arrayMergeRecursiveUnique($array1, $array2) { - if (empty($array1)) return $array2; //optimize the base case + if (empty($array1)) { + // Optimize the base case + return $array2; + } foreach ($array2 as $key => $value) { - if (is_array($value) && is_array(@$array1[$key])) { + if (is_array($value) && isset($array1[$key]) && is_array($array1[$key])) { $value = static::arrayMergeRecursiveUnique($array1[$key], $value); } $array1[$key] = $value; } + return $array1; } @@ -250,9 +298,9 @@ public static function truncateHtml($text, $length = 100, $ellipsis = '...') { if (mb_strlen($text) <= $length) { return $text; - } else { - return Truncator::truncateLetters($text, $length, $ellipsis); } + + return Truncator::truncateLetters($text, $length, $ellipsis); } /** @@ -278,7 +326,7 @@ public static function safeTruncateHtml($text, $length = 25, $ellipsis = '...') */ public static function generateRandomString($length = 5) { - return substr(str_shuffle("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"), 0, $length); + return substr(str_shuffle('0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'), 0, $length); } /** @@ -297,7 +345,7 @@ public static function download($file, $force_download = true, $sec = 0, $bytes Grav::instance()->fireEvent('onBeforeDownload', new Event(['file' => $file])); $file_parts = pathinfo($file); - $mimetype = Utils::getMimeByExtension($file_parts['extension']); + $mimetype = static::getMimeByExtension($file_parts['extension']); $size = filesize($file); // File size // clean all buffers @@ -310,7 +358,7 @@ public static function download($file, $force_download = true, $sec = 0, $bytes ini_set('zlib.output_compression', 'Off'); } - header("Content-Type: " . $mimetype); + header('Content-Type: ' . $mimetype); header('Accept-Ranges: bytes'); if ($force_download) { @@ -320,22 +368,23 @@ public static function download($file, $force_download = true, $sec = 0, $bytes // multipart-download and download resuming support if (isset($_SERVER['HTTP_RANGE'])) { - list($a, $range) = explode("=", $_SERVER['HTTP_RANGE'], 2); - list($range) = explode(",", $range, 2); - list($range, $range_end) = explode("-", $range); - $range = intval($range); + list($a, $range) = explode('=', $_SERVER['HTTP_RANGE'], 2); + list($range) = explode(',', $range, 2); + list($range, $range_end) = explode('-', $range); + $range = (int)$range; if (!$range_end) { $range_end = $size - 1; } else { - $range_end = intval($range_end); + $range_end = (int)$range_end; } $new_length = $range_end - $range + 1; - header("HTTP/1.1 206 Partial Content"); - header("Content-Length: $new_length"); - header("Content-Range: bytes $range-$range_end/$size"); + header('HTTP/1.1 206 Partial Content'); + header("Content-Length: {$new_length}"); + header("Content-Range: bytes {$range}-{$range_end}/{$size}"); } else { + $range = 0; $new_length = $size; - header("Content-Length: " . $size); + header('Content-Length: ' . $size); if (Grav::instance()['config']->get('system.cache.enabled')) { $expires = Grav::instance()['config']->get('system.pages.expires'); @@ -345,7 +394,7 @@ public static function download($file, $force_download = true, $sec = 0, $bytes header('Expires: ' . $expires_date); header('Pragma: cache'); } - header('Last-Modified: ' . gmdate("D, d M Y H:i:s T", filemtime($file))); + header('Last-Modified: ' . gmdate('D, d M Y H:i:s T', filemtime($file))); // Return 304 Not Modified if the file is already cached in the browser if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) && @@ -361,9 +410,9 @@ public static function download($file, $force_download = true, $sec = 0, $bytes $chunksize = $bytes * 8; //you may want to change this $bytes_send = 0; - $fp = @fopen($file, 'r'); + $fp = @fopen($file, 'rb'); if ($fp) { - if (isset($_SERVER['HTTP_RANGE'])) { + if ($range) { fseek($fp, $range); } while (!feof($fp) && (!connection_aborted()) && ($bytes_send < $new_length) ) { @@ -375,7 +424,7 @@ public static function download($file, $force_download = true, $sec = 0, $bytes } fclose($fp); } else { - throw new \Exception('Error - can not open file.'); + throw new \RuntimeException('Error - can not open file.'); } exit; @@ -449,13 +498,13 @@ public static function getExtensionByMime($mime, $default = 'html') return 'xml'; } - $media_types = Grav::instance()['config']->get('media.types'); + $media_types = (array)Grav::instance()['config']->get('media.types'); foreach ($media_types as $extension => $type) { - if ($extension == 'defaults') { + if ($extension === 'defaults') { continue; } - if (isset($type['mime']) && $type['mime'] == $mime) { + if (isset($type['mime']) && $type['mime'] === $mime) { return $extension; } } @@ -477,13 +526,13 @@ public static function normalizePath($path) $segments = explode('/', trim($path, '/')); $ret = []; foreach ($segments as $segment) { - if (($segment == '.') || strlen($segment) == 0) { + if (($segment === '.') || $segment === '') { continue; } - if ($segment == '..') { + if ($segment === '..') { array_pop($ret); } else { - array_push($ret, $segment); + $ret[] = $segment; } } @@ -499,7 +548,7 @@ public static function normalizePath($path) */ public static function isFunctionDisabled($function) { - return in_array($function, explode(',', ini_get('disable_functions'))); + return in_array($function, explode(',', ini_get('disable_functions')), true); } /** @@ -561,7 +610,7 @@ public static function arrayFilterRecursive(Array $source, $fn) /** * Flatten an array * - * @param $array + * @param array $array * @return array */ public static function arrayFlatten($array) @@ -594,7 +643,7 @@ public static function pathPrefixedByLangCode($string) $languages_enabled = Grav::instance()['config']->get('system.languages.supported', []); - if ($string[0] == '/' && $string[3] == '/' && in_array(substr($string, 1, 2), $languages_enabled)) { + if ($string[0] === '/' && $string[3] === '/' && in_array(substr($string, 1, 2), $languages_enabled)) { return true; } @@ -621,17 +670,21 @@ public static function date2timestamp($date, $format = null) $datetime = new DateTime($date); } - // fallback to strtotime if DateTime approach failed + // fallback to strtotime() if DateTime approach failed if ($datetime !== false) { return $datetime->getTimestamp(); - } else { - return strtotime($date); } + + return strtotime($date); } /** - * @deprecated Use getDotNotation() method instead + * @param array $array + * @param string $path + * @param null $default + * @return mixed * + * @deprecated Use getDotNotation() method instead */ public static function resolve(array $array, $path, $default = null) { @@ -711,7 +764,7 @@ private static function nonceTick() { $secondsInHalfADay = 60 * 60 * 12; - return (int)ceil(time() / ($secondsInHalfADay)); + return (int)ceil(time() / $secondsInHalfADay); } /** @@ -751,7 +804,7 @@ public static function getNonceOldStyle($action, $plusOneTick = false) /** * Verify the passed nonce for the give action * - * @param string $nonce the nonce to verify + * @param string|string[] $nonce the nonce to verify * @param string $action the action to verify the nonce to * * @return boolean verified or not @@ -764,26 +817,25 @@ public static function verifyNonce($nonce, $action) } //Nonce generated 0-12 hours ago - if ($nonce == self::getNonce($action)) { + if ($nonce === self::getNonce($action)) { return true; } //Nonce generated 12-24 hours ago $plusOneTick = true; - if ($nonce == self::getNonce($action, $plusOneTick)) { + 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)) { + if ($nonce === self::getNonceOldStyle($action)) { return true; } //Nonce generated 12-24 hours ago $plusOneTick = true; - if ($nonce == self::getNonceOldStyle($action, $plusOneTick)) { + if ($nonce === self::getNonceOldStyle($action, $plusOneTick)) { return true; } @@ -815,15 +867,16 @@ public static function isAdminPlugin() */ public static function getDotNotation($array, $key, $default = null) { - if (is_null($key)) return $array; + if (null === $key) { + return $array; + } - if (isset($array[$key])) return $array[$key]; + if (isset($array[$key])) { + return $array[$key]; + } - foreach (explode('.', $key) as $segment) - { - if ( ! is_array($array) || - ! array_key_exists($segment, $array)) - { + foreach (explode('.', $key) as $segment) { + if (!is_array($array) || !array_key_exists($segment, $array)) { return $default; } @@ -846,12 +899,13 @@ public static function getDotNotation($array, $key, $default = null) */ public static function setDotNotation(&$array, $key, $value, $merge = false) { - if (is_null($key)) return $array = $value; + if (null === $key) { + return $array = $value; + } $keys = explode('.', $key); - while (count($keys) > 1) - { + while (count($keys) > 1) { $key = array_shift($keys); if ( ! isset($array[$key]) || ! is_array($array[$key])) @@ -879,8 +933,9 @@ public static function setDotNotation(&$array, $key, $value, $merge = false) * * @return bool */ - public static function isWindows() { - return strncasecmp(PHP_OS, 'WIN', 3) == 0; + public static function isWindows() + { + return strncasecmp(PHP_OS, 'WIN', 3) === 0; } /** @@ -889,7 +944,7 @@ public static function isWindows() { * @return bool */ public static function isApache() { - return strpos($_SERVER["SERVER_SOFTWARE"], 'Apache') !== false; + return isset($_SERVER['SERVER_SOFTWARE']) && strpos($_SERVER['SERVER_SOFTWARE'], 'Apache') !== false; } /** @@ -899,7 +954,8 @@ public static function isApache() { * @param array $orderArray * @return array */ - public static function sortArrayByArray(array $array, array $orderArray) { + public static function sortArrayByArray(array $array, array $orderArray) + { $ordered = array(); foreach ($orderArray as $key) { if (array_key_exists($key, $array)) { @@ -914,8 +970,9 @@ public static function sortArrayByArray(array $array, array $orderArray) { * Get's path based on a token * * @param $path - * @param null $page + * @param Page|null $page * @return string + * @throws \RuntimeException */ public static function getPagePathFromToken($path, $page = null) { @@ -933,7 +990,7 @@ public static function getPagePathFromToken($path, $page = null) if ($matches) { if ($matches[1]) { - if (is_null($page)) { + if (null === $page) { throw new \RuntimeException('Page not available for this self@ reference'); } } elseif ($matches[2]) { @@ -992,10 +1049,10 @@ public static function parseSize($size) $unit = preg_replace('/[^bkmgtpezy]/i', '', $size); $size = preg_replace('/[^0-9\.]/', '', $size); if ($unit) { - return intval($size * pow(1024, stripos('bkmgtpezy', $unit[0]))); - } else { - return intval($size); + return (int)($size * pow(1024, stripos('bkmgtpezy', $unit[0]))); } + + return (int)$size; } /** @@ -1003,13 +1060,13 @@ public static function parseSize($size) * * @param $url * @return mixed + * @throws \InvalidArgumentException */ public static function multibyteParseUrl($url) { $enc_url = preg_replace_callback( '%[^:/@?&=#]+%usD', - function ($matches) - { + function ($matches) { return urlencode($matches[0]); }, $url @@ -1017,13 +1074,11 @@ function ($matches) $parts = parse_url($enc_url); - if($parts === false) - { + if($parts === false) { throw new \InvalidArgumentException('Malformed URL: ' . $url); } - foreach($parts as $name => $value) - { + foreach($parts as $name => $value) { $parts[$name] = urldecode($value); } diff --git a/system/src/Grav/Console/Cli/BackupCommand.php b/system/src/Grav/Console/Cli/BackupCommand.php index 4a0b754f3..4a538697b 100644 --- a/system/src/Grav/Console/Cli/BackupCommand.php +++ b/system/src/Grav/Console/Cli/BackupCommand.php @@ -2,7 +2,7 @@ /** * @package Grav.Console * - * @copyright Copyright (C) 2014 - 2017 RocketTheme, LLC. All rights reserved. + * @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Console/Cli/CleanCommand.php b/system/src/Grav/Console/Cli/CleanCommand.php index 6c1f11470..f6909fc85 100644 --- a/system/src/Grav/Console/Cli/CleanCommand.php +++ b/system/src/Grav/Console/Cli/CleanCommand.php @@ -2,7 +2,7 @@ /** * @package Grav.Console * - * @copyright Copyright (C) 2014 - 2017 RocketTheme, LLC. All rights reserved. + * @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Console/Cli/ClearCacheCommand.php b/system/src/Grav/Console/Cli/ClearCacheCommand.php index 18bff688e..cb5ffe84f 100644 --- a/system/src/Grav/Console/Cli/ClearCacheCommand.php +++ b/system/src/Grav/Console/Cli/ClearCacheCommand.php @@ -2,7 +2,7 @@ /** * @package Grav.Console * - * @copyright Copyright (C) 2014 - 2017 RocketTheme, LLC. All rights reserved. + * @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Console/Cli/ComposerCommand.php b/system/src/Grav/Console/Cli/ComposerCommand.php index b17b3f27e..4f9c18f63 100644 --- a/system/src/Grav/Console/Cli/ComposerCommand.php +++ b/system/src/Grav/Console/Cli/ComposerCommand.php @@ -2,7 +2,7 @@ /** * @package Grav.Console * - * @copyright Copyright (C) 2014 - 2017 RocketTheme, LLC. All rights reserved. + * @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Console/Cli/InstallCommand.php b/system/src/Grav/Console/Cli/InstallCommand.php index 53869fcc7..ac00256dd 100644 --- a/system/src/Grav/Console/Cli/InstallCommand.php +++ b/system/src/Grav/Console/Cli/InstallCommand.php @@ -2,7 +2,7 @@ /** * @package Grav.Console * - * @copyright Copyright (C) 2014 - 2017 RocketTheme, LLC. All rights reserved. + * @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Console/Cli/NewProjectCommand.php b/system/src/Grav/Console/Cli/NewProjectCommand.php index 61aa446a0..5a8f3d1e4 100644 --- a/system/src/Grav/Console/Cli/NewProjectCommand.php +++ b/system/src/Grav/Console/Cli/NewProjectCommand.php @@ -2,7 +2,7 @@ /** * @package Grav.Console * - * @copyright Copyright (C) 2014 - 2017 RocketTheme, LLC. All rights reserved. + * @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Console/Cli/SandboxCommand.php b/system/src/Grav/Console/Cli/SandboxCommand.php index 9fca627b1..284878e02 100644 --- a/system/src/Grav/Console/Cli/SandboxCommand.php +++ b/system/src/Grav/Console/Cli/SandboxCommand.php @@ -2,7 +2,7 @@ /** * @package Grav.Console * - * @copyright Copyright (C) 2014 - 2017 RocketTheme, LLC. All rights reserved. + * @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Console/ConsoleCommand.php b/system/src/Grav/Console/ConsoleCommand.php index 40fee6176..a9c221738 100644 --- a/system/src/Grav/Console/ConsoleCommand.php +++ b/system/src/Grav/Console/ConsoleCommand.php @@ -2,7 +2,7 @@ /** * @package Grav.Console * - * @copyright Copyright (C) 2014 - 2017 RocketTheme, LLC. All rights reserved. + * @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Console/ConsoleTrait.php b/system/src/Grav/Console/ConsoleTrait.php index 4ffdafb27..b6ab3bfe0 100644 --- a/system/src/Grav/Console/ConsoleTrait.php +++ b/system/src/Grav/Console/ConsoleTrait.php @@ -2,7 +2,7 @@ /** * @package Grav.Console * - * @copyright Copyright (C) 2014 - 2017 RocketTheme, LLC. All rights reserved. + * @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Console/Gpm/DirectInstallCommand.php b/system/src/Grav/Console/Gpm/DirectInstallCommand.php index 5444ba134..60c5fa893 100644 --- a/system/src/Grav/Console/Gpm/DirectInstallCommand.php +++ b/system/src/Grav/Console/Gpm/DirectInstallCommand.php @@ -2,7 +2,7 @@ /** * @package Grav.Console * - * @copyright Copyright (C) 2014 - 2017 RocketTheme, LLC. All rights reserved. + * @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Console/Gpm/IndexCommand.php b/system/src/Grav/Console/Gpm/IndexCommand.php index a1d15b621..8c457063f 100644 --- a/system/src/Grav/Console/Gpm/IndexCommand.php +++ b/system/src/Grav/Console/Gpm/IndexCommand.php @@ -2,7 +2,7 @@ /** * @package Grav.Console * - * @copyright Copyright (C) 2014 - 2017 RocketTheme, LLC. All rights reserved. + * @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Console/Gpm/InfoCommand.php b/system/src/Grav/Console/Gpm/InfoCommand.php index 1a9d4b669..8c1d50e83 100644 --- a/system/src/Grav/Console/Gpm/InfoCommand.php +++ b/system/src/Grav/Console/Gpm/InfoCommand.php @@ -2,7 +2,7 @@ /** * @package Grav.Console * - * @copyright Copyright (C) 2014 - 2017 RocketTheme, LLC. All rights reserved. + * @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Console/Gpm/InstallCommand.php b/system/src/Grav/Console/Gpm/InstallCommand.php index 5281b8b51..3281fd923 100644 --- a/system/src/Grav/Console/Gpm/InstallCommand.php +++ b/system/src/Grav/Console/Gpm/InstallCommand.php @@ -2,7 +2,7 @@ /** * @package Grav.Console * - * @copyright Copyright (C) 2014 - 2017 RocketTheme, LLC. All rights reserved. + * @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Console/Gpm/SelfupgradeCommand.php b/system/src/Grav/Console/Gpm/SelfupgradeCommand.php index e63ec56bc..d406a95b6 100644 --- a/system/src/Grav/Console/Gpm/SelfupgradeCommand.php +++ b/system/src/Grav/Console/Gpm/SelfupgradeCommand.php @@ -2,7 +2,7 @@ /** * @package Grav.Console * - * @copyright Copyright (C) 2014 - 2017 RocketTheme, LLC. All rights reserved. + * @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -102,8 +102,8 @@ protected function serve() if (!$this->upgrader->meetsRequirements()) { $this->output->writeln("ATTENTION:"); $this->output->writeln(" Grav has increased the minimum PHP requirement."); - $this->output->writeln(" You are currently running PHP " . PHP_VERSION . ", but PHP " . GRAV_PHP_MIN . " is required."); - $this->output->writeln(" Additional information: http://getgrav.org/blog/changing-php-requirements-to-5.5"); + $this->output->writeln(" You are currently running PHP " . phpversion() . ", but PHP " . $this->upgrader->minPHPVersion() . " is required."); + $this->output->writeln(" Additional information: http://getgrav.org/blog/changing-php-requirements"); $this->output->writeln(""); $this->output->writeln("Selfupgrade aborted."); $this->output->writeln(""); diff --git a/system/src/Grav/Console/Gpm/UninstallCommand.php b/system/src/Grav/Console/Gpm/UninstallCommand.php index e39d2e352..34b04ee4d 100644 --- a/system/src/Grav/Console/Gpm/UninstallCommand.php +++ b/system/src/Grav/Console/Gpm/UninstallCommand.php @@ -2,7 +2,7 @@ /** * @package Grav.Console * - * @copyright Copyright (C) 2014 - 2017 RocketTheme, LLC. All rights reserved. + * @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -185,7 +185,7 @@ private function uninstallPackage($slug, $package, $is_dependency = false) if (is_array($dependency)) { $dependency = $dependency['name']; } - if ($dependency === 'grav') { + if ($dependency === 'grav' || $dependency === 'php') { continue; } diff --git a/system/src/Grav/Console/Gpm/UpdateCommand.php b/system/src/Grav/Console/Gpm/UpdateCommand.php index 1fb07134b..06913b169 100644 --- a/system/src/Grav/Console/Gpm/UpdateCommand.php +++ b/system/src/Grav/Console/Gpm/UpdateCommand.php @@ -2,7 +2,7 @@ /** * @package Grav.Console * - * @copyright Copyright (C) 2014 - 2017 RocketTheme, LLC. All rights reserved. + * @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Console/Gpm/VersionCommand.php b/system/src/Grav/Console/Gpm/VersionCommand.php index 14a1fb71e..c828106c4 100644 --- a/system/src/Grav/Console/Gpm/VersionCommand.php +++ b/system/src/Grav/Console/Gpm/VersionCommand.php @@ -2,7 +2,7 @@ /** * @package Grav.Console * - * @copyright Copyright (C) 2014 - 2017 RocketTheme, LLC. All rights reserved. + * @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Console/TerminalObjects/Table.php b/system/src/Grav/Console/TerminalObjects/Table.php index 07aca144c..c3078a964 100644 --- a/system/src/Grav/Console/TerminalObjects/Table.php +++ b/system/src/Grav/Console/TerminalObjects/Table.php @@ -2,7 +2,7 @@ /** * @package Grav.Console * - * @copyright Copyright (C) 2014 - 2017 RocketTheme, LLC. All rights reserved. + * @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Framework/Cache/AbstractCache.php b/system/src/Grav/Framework/Cache/AbstractCache.php new file mode 100644 index 000000000..de749a1c6 --- /dev/null +++ b/system/src/Grav/Framework/Cache/AbstractCache.php @@ -0,0 +1,30 @@ +init($namespace, $defaultLifetime); + } +} diff --git a/system/src/Grav/Framework/Cache/Adapter/ChainCache.php b/system/src/Grav/Framework/Cache/Adapter/ChainCache.php new file mode 100644 index 000000000..041b676f2 --- /dev/null +++ b/system/src/Grav/Framework/Cache/Adapter/ChainCache.php @@ -0,0 +1,197 @@ +caches = array_values($caches); + $this->count = count($caches); + } + + /** + * @inheritdoc + */ + public function doGet($key, $miss) + { + foreach ($this->caches as $i => $cache) { + $value = $cache->doGet($key, $miss); + if ($value !== $miss) { + while (--$i >= 0) { + // Update all the previous caches with missing value. + $this->caches[$i]->doSet($key, $value, $this->getDefaultLifetime()); + } + + return $value; + } + } + + return $miss; + } + + /** + * @inheritdoc + */ + public function doSet($key, $value, $ttl) + { + $success = true; + $i = $this->count; + + while ($i--) { + $success = $this->caches[$i]->doSet($key, $value, $ttl) && $success; + } + + return $success; + } + + /** + * @inheritdoc + */ + public function doDelete($key) + { + $success = true; + $i = $this->count; + + while ($i--) { + $success = $this->caches[$i]->doDelete($key) && $success; + } + + return $success; + } + + /** + * @inheritdoc + */ + public function doClear() + { + $success = true; + $i = $this->count; + + while ($i--) { + $success = $this->caches[$i]->doClear() && $success; + } + return $success; + } + + /** + * @inheritdoc + */ + public function doGetMultiple($keys, $miss) + { + $list = []; + foreach ($this->caches as $i => $cache) { + $list[$i] = $cache->doGetMultiple($keys, $miss); + + $keys = array_diff_key($keys, $list[$i]); + + if (!$keys) { + break; + } + } + + $values = []; + // Update all the previous caches with missing values. + foreach (array_reverse($list) as $i => $items) { + $values += $items; + if ($i && $values) { + $this->caches[$i-1]->doSetMultiple($values, $this->getDefaultLifetime()); + } + } + + return $values; + } + + /** + * @inheritdoc + */ + public function doSetMultiple($values, $ttl) + { + $success = true; + $i = $this->count; + + while ($i--) { + $success = $this->caches[$i]->doSetMultiple($values, $ttl) && $success; + } + + return $success; + } + + /** + * @inheritdoc + */ + public function doDeleteMultiple($keys) + { + $success = true; + $i = $this->count; + + while ($i--) { + $success = $this->caches[$i]->doDeleteMultiple($keys) && $success; + } + + return $success; + } + + /** + * @inheritdoc + */ + public function doHas($key) + { + foreach ($this->caches as $cache) { + if ($cache->doHas($key)) { + return true; + } + } + + return false; + } +} diff --git a/system/src/Grav/Framework/Cache/Adapter/DoctrineCache.php b/system/src/Grav/Framework/Cache/Adapter/DoctrineCache.php new file mode 100644 index 000000000..3a69c1ff9 --- /dev/null +++ b/system/src/Grav/Framework/Cache/Adapter/DoctrineCache.php @@ -0,0 +1,123 @@ +getNamespace(); + $namespace && $doctrineCache->setNamespace($namespace); + + $this->driver = $doctrineCache; + } + + /** + * @inheritdoc + */ + public function doGet($key, $miss) + { + $value = $this->driver->fetch($key); + + // Doctrine cache does not differentiate between no result and cached 'false'. Make sure that we do. + return $value !== false || $this->driver->contains($key) ? $value : $miss; + } + + /** + * @inheritdoc + */ + public function doSet($key, $value, $ttl) + { + return $this->driver->save($key, $value, (int) $ttl); + } + + /** + * @inheritdoc + */ + public function doDelete($key) + { + return $this->driver->delete($key); + } + + /** + * @inheritdoc + */ + public function doClear() + { + return $this->driver->deleteAll(); + } + + /** + * @inheritdoc + */ + public function doGetMultiple($keys, $miss) + { + return $this->driver->fetchMultiple($keys); + } + + /** + * @inheritdoc + */ + public function doSetMultiple($values, $ttl) + { + return $this->driver->saveMultiple($values, (int) $ttl); + } + + /** + * @inheritdoc + * @throws \Psr\SimpleCache\InvalidArgumentException + */ + public function doDeleteMultiple($keys) + { + // TODO: Remove when Doctrine Cache has been updated to support the feature. + if (!method_exists($this->driver, 'deleteMultiple')) { + $success = true; + foreach ($keys as $key) { + $success = $this->delete($key) && $success; + } + + return $success; + } + + return $this->driver->deleteMultiple($keys); + } + + /** + * @inheritdoc + */ + public function doHas($key) + { + return $this->driver->contains($key); + } +} diff --git a/system/src/Grav/Framework/Cache/Adapter/FileCache.php b/system/src/Grav/Framework/Cache/Adapter/FileCache.php new file mode 100644 index 000000000..389b626e1 --- /dev/null +++ b/system/src/Grav/Framework/Cache/Adapter/FileCache.php @@ -0,0 +1,210 @@ +getFile($key); + + if (!file_exists($file) || !$h = @fopen($file, 'rb')) { + return $miss; + } + + if ($now >= (int) $expiresAt = fgets($h)) { + fclose($h); + @unlink($file); + } else { + $i = rawurldecode(rtrim(fgets($h))); + $value = stream_get_contents($h); + fclose($h); + + if ($i === $key) { + return unserialize($value); + } + } + + return $miss; + } + + /** + * @inheritdoc + * @throws \Psr\SimpleCache\CacheException + */ + public function doSet($key, $value, $ttl) + { + $expiresAt = time() + (int)$ttl; + + $result = $this->write( + $this->getFile($key, true), + $expiresAt . "\n" . rawurlencode($key) . "\n" . serialize($value), + $expiresAt + ); + + if (!$result && !is_writable($this->directory)) { + throw new CacheException(sprintf('Cache directory is not writable (%s)', $this->directory)); + } + + return $result; + } + + /** + * @inheritdoc + */ + public function doDelete($key) + { + $file = $this->getFile($key); + + return (!file_exists($file) || @unlink($file) || !file_exists($file)); + } + + /** + * @inheritdoc + */ + public function doClear() + { + $result = true; + $iterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($this->directory, \FilesystemIterator::SKIP_DOTS)); + + foreach ($iterator as $file) { + $result = ($file->isDir() || @unlink($file) || !file_exists($file)) && $result; + } + + return $result; + } + + /** + * @inheritdoc + */ + public function doHas($key) + { + $file = $this->getFile($key); + + return file_exists($file) && (@filemtime($file) > time() || $this->doGet($key, null)); + } + + /** + * @param string $key + * @param bool $mkdir + * @return string + */ + protected function getFile($key, $mkdir = false) + { + $hash = str_replace('/', '-', base64_encode(hash('sha256', static::class . $key, true))); + $dir = $this->directory . $hash[0] . DIRECTORY_SEPARATOR . $hash[1] . DIRECTORY_SEPARATOR; + + if ($mkdir && !file_exists($dir)) { + @mkdir($dir, 0777, true); + } + + return $dir . substr($hash, 2, 20); + } + + /** + * @param string $namespace + * @param string $directory + * @throws \Psr\SimpleCache\InvalidArgumentException + */ + private function init($namespace, $directory) + { + if (!isset($directory[0])) { + $directory = sys_get_temp_dir() . '/grav-cache'; + } else { + $directory = realpath($directory) ?: $directory; + } + + if (isset($namespace[0])) { + if (preg_match('#[^-+_.A-Za-z0-9]#', $namespace, $match)) { + throw new InvalidArgumentException(sprintf('Namespace contains "%s" but only characters in [-+_.A-Za-z0-9] are allowed.', $match[0])); + } + $directory .= DIRECTORY_SEPARATOR . $namespace; + } + + if (!file_exists($directory)) { + @mkdir($directory, 0777, true); + } + + $directory .= DIRECTORY_SEPARATOR; + // On Windows the whole path is limited to 258 chars + if ('\\' === DIRECTORY_SEPARATOR && strlen($directory) > 234) { + throw new InvalidArgumentException(sprintf('Cache folder is too long (%s)', $directory)); + } + $this->directory = $directory; + } + + /** + * @param string $file + * @param string $data + * @param int|null $expiresAt + * @return bool + */ + private function write($file, $data, $expiresAt = null) + { + set_error_handler(__CLASS__.'::throwError'); + + try { + if ($this->tmp === null) { + $this->tmp = $this->directory . uniqid('', true); + } + + file_put_contents($this->tmp, $data); + + if ($expiresAt !== null) { + touch($this->tmp, $expiresAt); + } + + return rename($this->tmp, $file); + } finally { + restore_error_handler(); + } + } + + /** + * @internal + * @throws \ErrorException + */ + public static function throwError($type, $message, $file, $line) + { + throw new \ErrorException($message, 0, $type, $file, $line); + } + + public function __destruct() + { + if ($this->tmp !== null && file_exists($this->tmp)) { + unlink($this->tmp); + } + } +} diff --git a/system/src/Grav/Framework/Cache/Adapter/MemoryCache.php b/system/src/Grav/Framework/Cache/Adapter/MemoryCache.php new file mode 100644 index 000000000..84911fbe1 --- /dev/null +++ b/system/src/Grav/Framework/Cache/Adapter/MemoryCache.php @@ -0,0 +1,61 @@ +cache)) { + return $miss; + } + + return $this->cache[$key]; + } + + public function doSet($key, $value, $ttl) + { + $this->cache[$key] = $value; + + return true; + } + + public function doDelete($key) + { + unset($this->cache[$key]); + + return true; + } + + public function doClear() + { + $this->cache = []; + + return true; + } + + public function doHas($key) + { + return array_key_exists($key, $this->cache); + } +} diff --git a/system/src/Grav/Framework/Cache/Adapter/SessionCache.php b/system/src/Grav/Framework/Cache/Adapter/SessionCache.php new file mode 100644 index 000000000..b1e9e9e51 --- /dev/null +++ b/system/src/Grav/Framework/Cache/Adapter/SessionCache.php @@ -0,0 +1,78 @@ +doGetStored($key); + + return $stored ? $stored[self::VALUE] : $miss; + } + + public function doSet($key, $value, $ttl) + { + $stored = [self::VALUE => $value]; + if (null !== $ttl) { + $stored[self::LIFETIME] = time() + $ttl; + + } + + $_SESSION[$this->getNamespace()][$key] = $stored; + + return true; + } + + public function doDelete($key) + { + unset($_SESSION[$this->getNamespace()][$key]); + + return true; + } + + public function doClear() + { + unset($_SESSION[$this->getNamespace()]); + + return true; + } + + public function doHas($key) + { + return $this->doGetStored($key) !== null; + } + + public function getNamespace() + { + return 'cache-' . parent::getNamespace(); + } + + protected function doGetStored($key) + { + $stored = isset($_SESSION[$this->getNamespace()][$key]) ? $_SESSION[$this->getNamespace()][$key] : null; + + if (isset($stored[self::LIFETIME]) && $stored[self::LIFETIME] < time()) { + unset($_SESSION[$this->getNamespace()][$key]); + $stored = null; + } + + return $stored ?: null; + } +} diff --git a/system/src/Grav/Framework/Cache/CacheInterface.php b/system/src/Grav/Framework/Cache/CacheInterface.php new file mode 100644 index 000000000..54af40c69 --- /dev/null +++ b/system/src/Grav/Framework/Cache/CacheInterface.php @@ -0,0 +1,27 @@ +namespace = (string) $namespace; + $this->defaultLifetime = $this->convertTtl($defaultLifetime); + $this->miss = new \stdClass; + } + + /** + * @return string + */ + protected function getNamespace() + { + return $this->namespace; + } + + /** + * @return int|null + */ + protected function getDefaultLifetime() + { + return $this->defaultLifetime; + } + + /** + * @inheritdoc + * @throws \Psr\SimpleCache\InvalidArgumentException + */ + public function get($key, $default = null) + { + $this->validateKey($key); + + $value = $this->doGet($key, $this->miss); + + return $value !== $this->miss ? $value : $default; + } + + /** + * @inheritdoc + * @throws \Psr\SimpleCache\InvalidArgumentException + */ + public function set($key, $value, $ttl = null) + { + $this->validateKey($key); + + $ttl = $this->convertTtl($ttl); + + // If a negative or zero TTL is provided, the item MUST be deleted from the cache. + return null !== $ttl && $ttl <= 0 ? $this->doDelete($key) : $this->doSet($key, $value, $ttl); + } + + /** + * @inheritdoc + * @throws \Psr\SimpleCache\InvalidArgumentException + */ + public function delete($key) + { + $this->validateKey($key); + + return $this->doDelete($key); + } + + /** + * @inheritdoc + */ + public function clear() + { + return $this->doClear(); + } + + /** + * @inheritdoc + * @throws \Psr\SimpleCache\InvalidArgumentException + */ + public function getMultiple($keys, $default = null) + { + if ($keys instanceof \Traversable) { + $keys = iterator_to_array($keys, false); + } elseif (!is_array($keys)) { + throw new InvalidArgumentException( + sprintf( + 'Cache keys must be array or Traversable, "%s" given', + is_object($keys) ? get_class($keys) : gettype($keys) + ) + ); + } + + if (empty($keys)) { + return []; + } + + $this->validateKeys($keys); + $keys = array_unique($keys); + $keys = array_combine($keys, $keys); + + $list = $this->doGetMultiple($keys, $this->miss); + + // Make sure that values are returned in the same order as the keys were given. + $values = []; + foreach ($keys as $key) { + if (!array_key_exists($key, $list) || $list[$key] === $this->miss) { + $values[$key] = $default; + } else { + $values[$key] = $list[$key]; + } + } + + return $values; + } + + /** + * @inheritdoc + * @throws \Psr\SimpleCache\InvalidArgumentException + */ + public function setMultiple($values, $ttl = null) + { + if ($values instanceof \Traversable) { + $values = iterator_to_array($values, true); + } elseif (!is_array($values)) { + throw new InvalidArgumentException( + sprintf( + 'Cache values must be array or Traversable, "%s" given', + is_object($values) ? get_class($values) : gettype($values) + ) + ); + } + + $keys = array_keys($values); + + if (empty($keys)) { + return true; + } + + $this->validateKeys($keys); + + $ttl = $this->convertTtl($ttl); + + // If a negative or zero TTL is provided, the item MUST be deleted from the cache. + return null !== $ttl && $ttl <= 0 ? $this->doDeleteMultiple($keys) : $this->doSetMultiple($values, $ttl); + } + + /** + * @inheritdoc + * @throws \Psr\SimpleCache\InvalidArgumentException + */ + public function deleteMultiple($keys) + { + if ($keys instanceof \Traversable) { + $keys = iterator_to_array($keys, false); + } elseif (!is_array($keys)) { + throw new InvalidArgumentException( + sprintf( + 'Cache keys must be array or Traversable, "%s" given', + is_object($keys) ? get_class($keys) : gettype($keys) + ) + ); + } + + if (empty($keys)) { + return true; + } + + $this->validateKeys($keys); + + return $this->doDeleteMultiple($keys); + } + + /** + * @inheritdoc + * @throws \Psr\SimpleCache\InvalidArgumentException + */ + public function has($key) + { + $this->validateKey($key); + + return $this->doHas($key); + } + + abstract public function doGet($key, $miss); + abstract public function doSet($key, $value, $ttl); + abstract public function doDelete($key); + abstract public function doClear(); + + /** + * @param array $keys + * @param mixed $miss + * @return array + */ + public function doGetMultiple($keys, $miss) + { + $results = []; + + foreach ($keys as $key) { + $value = $this->doGet($key, $miss); + if ($value !== $miss) { + $results[$key] = $value; + } + } + + return $results; + } + + /** + * @param array $values + * @param int $ttl + * @return bool + */ + public function doSetMultiple($values, $ttl) + { + $success = true; + + foreach ($values as $key => $value) { + $success = $this->doSet($key, $value, $ttl) && $success; + } + + return $success; + } + + /** + * @param array $keys + * @return bool + */ + public function doDeleteMultiple($keys) + { + $success = true; + + foreach ($keys as $key) { + $success = $this->doDelete($key) && $success; + } + + return $success; + } + + abstract public function doHas($key); + + /** + * @param string $key + * @throws \Psr\SimpleCache\InvalidArgumentException + */ + protected function validateKey($key) + { + if (!is_string($key)) { + throw new InvalidArgumentException( + sprintf( + 'Cache key must be string, "%s" given', + is_object($key) ? get_class($key) : gettype($key) + ) + ); + } + if (!isset($key[0])) { + throw new InvalidArgumentException('Cache key length must be greater than zero'); + } + if (strlen($key) > 64) { + throw new InvalidArgumentException( + sprintf('Cache key length must be less than 65 characters, key had %s characters', strlen($key)) + ); + } + if (strpbrk($key, '{}()/\@:') !== false) { + throw new InvalidArgumentException( + sprintf('Cache key "%s" contains reserved characters {}()/\@:', $key) + ); + } + } + + /** + * @param array $keys + * @throws \Psr\SimpleCache\InvalidArgumentException + */ + protected function validateKeys($keys) + { + foreach ($keys as $key) { + $this->validateKey($key); + } + } + + /** + * @param null|int|\DateInterval $ttl + * @return int|null + * @throws \Psr\SimpleCache\InvalidArgumentException + */ + protected function convertTtl($ttl) + { + if ($ttl === null) { + return $this->getDefaultLifetime(); + } + + if (is_int($ttl)) { + return $ttl; + } + + if ($ttl instanceof \DateInterval) { + $ttl = (int) \DateTime::createFromFormat('U', 0)->add($ttl)->format('U'); + } + + throw new InvalidArgumentException( + sprintf( + 'Expiration date must be an integer, a DateInterval or null, "%s" given', + is_object($ttl) ? get_class($ttl) : gettype($ttl) + ) + ); + } +} diff --git a/system/src/Grav/Framework/Cache/Exception/CacheException.php b/system/src/Grav/Framework/Cache/Exception/CacheException.php new file mode 100644 index 000000000..71caff7f6 --- /dev/null +++ b/system/src/Grav/Framework/Cache/Exception/CacheException.php @@ -0,0 +1,19 @@ +path = $path; + $this->flags = self::INCLUDE_FILES | self::INCLUDE_FOLDERS; + $this->nestingLimit = 0; + $this->createObjectFunction = [$this, 'createObject']; + + $this->setIterator(); + } + + /** + * @return string + */ + public function getPath() + { + return $this->path; + } + + /** + * @param Criteria $criteria + * @return ArrayCollection + * @todo Implement lazy matching + */ + public function matching(Criteria $criteria) + { + $expr = $criteria->getWhereExpression(); + + $oldFilter = $this->filterFunction; + if ($expr) { + $visitor = new ClosureExpressionVisitor(); + $filter = $visitor->dispatch($expr); + $this->addFilter($filter); + } + + $filtered = $this->doInitializeByIterator($this->iterator, $this->nestingLimit); + $this->filterFunction = $oldFilter; + + if ($orderings = $criteria->getOrderings()) { + $next = null; + foreach (array_reverse($orderings) as $field => $ordering) { + $next = ClosureExpressionVisitor::sortByField($field, $ordering == Criteria::DESC ? -1 : 1, $next); + } + + uasort($filtered, $next); + } else { + ksort($filtered); + } + + $offset = $criteria->getFirstResult(); + $length = $criteria->getMaxResults(); + + if ($offset || $length) { + $filtered = array_slice($filtered, (int)$offset, $length); + } + + return new ArrayCollection($filtered); + } + + protected function setIterator() + { + $iteratorFlags = \RecursiveDirectoryIterator::SKIP_DOTS + \FilesystemIterator::UNIX_PATHS + + \FilesystemIterator::CURRENT_AS_SELF + \FilesystemIterator::FOLLOW_SYMLINKS; + + if (strpos($this->path, '://')) { + /** @var UniformResourceLocator $locator */ + $locator = Grav::instance()['locator']; + $this->iterator = $locator->getRecursiveIterator($this->path, $iteratorFlags); + } else { + $this->iterator = new \RecursiveDirectoryIterator($this->path, $iteratorFlags); + } + } + + /** + * @param callable $filterFunction + * @return $this + */ + protected function addFilter(callable $filterFunction) + { + if ($this->filterFunction) { + $oldFilterFunction = $this->filterFunction; + $this->filterFunction = function ($expr) use ($oldFilterFunction, $filterFunction) { + return $oldFilterFunction($expr) && $filterFunction($expr); + }; + } else { + $this->filterFunction = $filterFunction; + } + + return $this; + } + + /** + * {@inheritDoc} + */ + protected function doInitialize() + { + $filtered = $this->doInitializeByIterator($this->iterator, $this->nestingLimit); + ksort($filtered); + + $this->collection = new ArrayCollection($filtered); + } + + protected function doInitializeByIterator(\SeekableIterator $iterator, $nestingLimit) + { + $children = []; + $objects = []; + $filter = $this->filterFunction; + $objectFunction = $this->createObjectFunction; + + /** @var \RecursiveDirectoryIterator $file */ + foreach ($iterator as $file) { + // Skip files if they shouldn't be included. + if (!($this->flags & static::INCLUDE_FILES) && $file->isFile()) { + continue; + } + + // Apply main filter. + if ($filter && !$filter($file)) { + continue; + } + + // Include children if the recursive flag is set. + if (($this->flags & static::RECURSIVE) && $nestingLimit > 0 && $file->hasChildren()) { + $children[] = $file->getChildren(); + } + + // Skip folders if they shouldn't be included. + if (!($this->flags & static::INCLUDE_FOLDERS) && $file->isDir()) { + continue; + } + + $object = $objectFunction($file); + $objects[$object->key] = $object; + } + + if ($children) { + $objects += $this->doInitializeChildren($children, $nestingLimit - 1); + } + + return $objects; + } + + /** + * @param \RecursiveDirectoryIterator[] $children + * @return array + */ + protected function doInitializeChildren(array $children, $nestingLimit) + { + $objects = []; + + foreach ($children as $iterator) { + $objects += $this->doInitializeByIterator($iterator, $nestingLimit); + } + + return $objects; + } + + /** + * @param \RecursiveDirectoryIterator $file + * @return object + */ + protected function createObject($file) + { + return (object) [ + 'key' => $file->getSubPathname(), + 'type' => $file->isDir() ? 'folder' : 'file:' . $file->getExtension(), + 'url' => method_exists($file, 'getUrl') ? $file->getUrl() : null, + 'pathname' => $file->getPathname(), + 'mtime' => $file->getMTime() + ]; + } +} diff --git a/system/src/Grav/Framework/Collection/AbstractLazyCollection.php b/system/src/Grav/Framework/Collection/AbstractLazyCollection.php index ad0830d94..af2f696f8 100644 --- a/system/src/Grav/Framework/Collection/AbstractLazyCollection.php +++ b/system/src/Grav/Framework/Collection/AbstractLazyCollection.php @@ -2,7 +2,7 @@ /** * @package Grav\Framework\Collection * - * @copyright Copyright (C) 2014 - 2017 RocketTheme, LLC. All rights reserved. + * @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -42,6 +42,15 @@ public function shuffle() return $this->collection->shuffle(); } + /** + * {@inheritDoc} + */ + public function chunk($size) + { + $this->initialize(); + return $this->collection->chunk($size); + } + /** * {@inheritDoc} */ diff --git a/system/src/Grav/Framework/Collection/ArrayCollection.php b/system/src/Grav/Framework/Collection/ArrayCollection.php index 917abd6b7..bf6dda056 100644 --- a/system/src/Grav/Framework/Collection/ArrayCollection.php +++ b/system/src/Grav/Framework/Collection/ArrayCollection.php @@ -2,7 +2,7 @@ /** * @package Grav\Framework\Collection * - * @copyright Copyright (C) 2014 - 2017 RocketTheme, LLC. All rights reserved. + * @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -24,12 +24,12 @@ class ArrayCollection extends BaseArrayCollection implements CollectionInterface */ public function reverse() { - if (method_exists($this, 'createFrom')) { - return $this->createFrom(array_reverse($this->toArray())); - } else { - // TODO: remove when PHP 5.6 is minimum (with doctrine/collections v1.4). + // 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,12 +42,23 @@ public function shuffle() $keys = $this->getKeys(); shuffle($keys); - if (method_exists($this, 'createFrom')) { - return $this->createFrom(array_replace(array_flip($keys), $this->toArray())); - } else { - // TODO: remove when PHP 5.6 is minimum (with doctrine/collections v1.4). + // 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())); + } + + /** + * Split collection into chunks. + * + * @param int $size Size of each chunk. + * @return array + */ + public function chunk($size) + { + return array_chunk($this->toArray(), $size, true); } /** diff --git a/system/src/Grav/Framework/Collection/CollectionInterface.php b/system/src/Grav/Framework/Collection/CollectionInterface.php index ef46e00ec..5f5e1fc60 100644 --- a/system/src/Grav/Framework/Collection/CollectionInterface.php +++ b/system/src/Grav/Framework/Collection/CollectionInterface.php @@ -2,7 +2,7 @@ /** * @package Grav\Framework\Collection * - * @copyright Copyright (C) 2014 - 2017 RocketTheme, LLC. All rights reserved. + * @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -30,4 +30,12 @@ public function reverse(); * @return static */ public function shuffle(); + + /** + * Split collection into chunks. + * + * @param int $size Size of each chunk. + * @return array + */ + public function chunk($size); } diff --git a/system/src/Grav/Framework/Collection/FileCollection.php b/system/src/Grav/Framework/Collection/FileCollection.php index da9764448..9c1d905fe 100644 --- a/system/src/Grav/Framework/Collection/FileCollection.php +++ b/system/src/Grav/Framework/Collection/FileCollection.php @@ -2,67 +2,28 @@ /** * @package Grav\Framework\Collection * - * @copyright Copyright (C) 2014 - 2017 RocketTheme, LLC. All rights reserved. + * @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ namespace Grav\Framework\Collection; -use Doctrine\Common\Collections\Criteria; -use Doctrine\Common\Collections\Expr\ClosureExpressionVisitor; -use Grav\Common\Grav; -use RocketTheme\Toolbox\ResourceLocator\RecursiveUniformResourceIterator; -use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator; - /** * Collection of objects stored into a filesystem. * * @package Grav\Framework\Collection */ -class FileCollection extends AbstractLazyCollection +class FileCollection extends AbstractFileCollection { - const INCLUDE_FILES = 1; - const INCLUDE_FOLDERS = 2; - const RECURSIVE = 4; - - /** - * @var string - */ - protected $path; - - /** - * @var \RecursiveDirectoryIterator|RecursiveUniformResourceIterator - */ - protected $iterator; - - /** - * @var callable - */ - protected $createObjectFunction; - - /** - * @var callable - */ - protected $filterFunction; - - /** - * @var int - */ - protected $flags; - - /** - * @var int - */ - protected $nestingLimit; - /** * @param string $path * @param int $flags */ public function __construct($path, $flags = null) { - $this->path = $path; - $this->flags = (int) ($flags ?: FileCollection::INCLUDE_FILES | FileCollection::INCLUDE_FOLDERS | FileCollection::RECURSIVE); + parent::__construct($path); + + $this->flags = (int)($flags ?: self::INCLUDE_FILES | self::INCLUDE_FOLDERS | self::RECURSIVE); $this->setIterator(); $this->setFilter(); @@ -70,14 +31,6 @@ public function __construct($path, $flags = null) $this->setNestingLimit(); } - /** - * @return string - */ - public function getPath() - { - return $this->path; - } - /** * @return int */ @@ -94,18 +47,15 @@ public function getNestingLimit() return $this->nestingLimit; } - public function setIterator() + /** + * @param int $limit + * @return $this + */ + public function setNestingLimit($limit = 99) { - $iteratorFlags = \RecursiveDirectoryIterator::SKIP_DOTS + \FilesystemIterator::UNIX_PATHS - + \FilesystemIterator::CURRENT_AS_SELF + \FilesystemIterator::FOLLOW_SYMLINKS; + $this->nestingLimit = (int) $limit; - if (strpos($this->path, '://')) { - /** @var UniformResourceLocator $locator */ - $locator = Grav::instance()['locator']; - $this->iterator = $locator->getRecursiveIterator($this->path, $iteratorFlags); - } else { - $this->iterator = new \RecursiveDirectoryIterator($this->path, $iteratorFlags); - } + return $this; } /** @@ -125,14 +75,7 @@ public function setFilter(callable $filterFunction = null) */ public function addFilter(callable $filterFunction) { - if ($this->filterFunction) { - $oldFilterFunction = $this->filterFunction; - $this->filterFunction = function ($expr) use ($oldFilterFunction, $filterFunction) { - return $oldFilterFunction($expr) && $filterFunction($expr); - }; - } else { - $this->filterFunction = $filterFunction; - } + parent::addFilter($filterFunction); return $this; } @@ -147,132 +90,4 @@ public function setObjectBuilder(callable $objectFunction = null) return $this; } - - public function setNestingLimit($limit = 99) - { - $this->nestingLimit = (int) $limit; - - return $this; - } - - /** - * @param Criteria $criteria - * @return ArrayCollection - * @todo Implement lazy matching - */ - public function matching(Criteria $criteria) - { - $expr = $criteria->getWhereExpression(); - - $oldFilter = $this->filterFunction; - if ($expr) { - $visitor = new ClosureExpressionVisitor(); - $filter = $visitor->dispatch($expr); - $this->addFilter($filter); - } - - $filtered = $this->doInitializeByIterator($this->iterator, $this->nestingLimit); - $this->filterFunction = $oldFilter; - - if ($orderings = $criteria->getOrderings()) { - $next = null; - foreach (array_reverse($orderings) as $field => $ordering) { - $next = ClosureExpressionVisitor::sortByField($field, $ordering == Criteria::DESC ? -1 : 1, $next); - } - - uasort($filtered, $next); - } else { - ksort($filtered); - } - - $offset = $criteria->getFirstResult(); - $length = $criteria->getMaxResults(); - - if ($offset || $length) { - $filtered = array_slice($filtered, (int)$offset, $length); - } - - return new ArrayCollection($filtered); - } - - /** - * {@inheritDoc} - */ - protected function doInitialize() - { - $filtered = $this->doInitializeByIterator($this->iterator, $this->nestingLimit); - ksort($filtered); - - $this->collection = new ArrayCollection($filtered); - } - - protected function doInitializeByIterator(\SeekableIterator $iterator, $nestingLimit) - { - $children = []; - $objects = []; - $filter = $this->filterFunction; - $objectFunction = $this->createObjectFunction; - - /** @var \RecursiveDirectoryIterator $file */ - foreach ($iterator as $file) { - // Skip files if they shouldn't be included. - if (!($this->flags & static::INCLUDE_FILES) && $file->isFile()) { - continue; - } - - // Apply main filter. - if ($filter && !$filter($file)) { - continue; - } - - // Include children if the recursive flag is set. - if (($this->flags & static::RECURSIVE) && $nestingLimit > 0 && $file->hasChildren()) { - $children[] = $file->getChildren(); - } - - // Skip folders if they shouldn't be included. - if (!($this->flags & static::INCLUDE_FOLDERS) && $file->isDir()) { - continue; - } - - $object = $objectFunction($file); - $objects[$object->key] = $object; - } - - if ($children) { - $objects += $this->doInitializeChildren($children, $nestingLimit - 1); - } - - return $objects; - } - - /** - * @param \RecursiveDirectoryIterator[] $children - * @return array - */ - protected function doInitializeChildren(array $children, $nestingLimit) - { - $objects = []; - - foreach ($children as $iterator) { - $objects += $this->doInitializeByIterator($iterator, $nestingLimit); - } - - return $objects; - } - - /** - * @param \RecursiveDirectoryIterator $file - * @return object - */ - protected function createObject($file) - { - return (object) [ - 'key' => $file->getSubPathName(), - 'type' => $file->isDir() ? 'folder' : 'file:' . $file->getExtension(), - 'url' => method_exists($file, 'getUrl') ? $file->getUrl() : null, - 'pathname' => $file->getPathname(), - 'mtime' => $file->getMTime() - ]; - } } diff --git a/system/src/Grav/Framework/Collection/FileCollectionInterface.php b/system/src/Grav/Framework/Collection/FileCollectionInterface.php new file mode 100644 index 000000000..37f63d2cc --- /dev/null +++ b/system/src/Grav/Framework/Collection/FileCollectionInterface.php @@ -0,0 +1,28 @@ +setContent('my inner content'); + * $outerBlock = ContentBlock::create(); + * $outerBlock->setContent(sprintf('Inside my outer block I have %s.', $innerBlock->getToken())); + * $outerBlock->addBlock($innerBlock); + * echo $outerBlock; + * + * @package Grav\Framework\ContentBlock + */ +class ContentBlock implements ContentBlockInterface +{ + protected $version = 1; + protected $id; + protected $tokenTemplate = '@@BLOCK-%s@@'; + protected $content = ''; + protected $blocks = []; + + /** + * @param string $id + * @return static + */ + public static function create($id = null) + { + return new static($id); + } + + /** + * @param array $serialized + * @return ContentBlockInterface + */ + public static function fromArray(array $serialized) + { + try { + $type = isset($serialized['_type']) ? $serialized['_type'] : null; + $id = isset($serialized['id']) ? $serialized['id'] : null; + + if (!$type || !$id || !is_a($type, 'Grav\Framework\ContentBlock\ContentBlockInterface', true)) { + throw new \RuntimeException('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); + } + + return $instance; + } + + /** + * Block constructor. + * + * @param string $id + */ + public function __construct($id = null) + { + $this->id = $id ? (string) $id : $this->generateId(); + } + + /** + * @return string + */ + public function getId() + { + return $this->id; + } + + /** + * @return string + */ + public function getToken() + { + return sprintf($this->tokenTemplate, $this->getId()); + } + + /** + * @return array + */ + public function toArray() + { + $blocks = []; + /** + * @var string $id + * @var ContentBlockInterface $block + */ + foreach ($this->blocks as $block) { + $blocks[$block->getId()] = $block->toArray(); + } + + $array = [ + '_type' => get_class($this), + '_version' => $this->version, + 'id' => $this->id, + ]; + + if ($this->content) { + $array['content'] = $this->content; + } + + if ($blocks) { + $array['blocks'] = $blocks; + } + + return $array; + } + + /** + * @return string + */ + public function toString() + { + if (!$this->blocks) { + return (string) $this->content; + } + + $tokens = []; + $replacements = []; + foreach ($this->blocks as $block) { + $tokens[] = $block->getToken(); + $replacements[] = $block->toString(); + } + + return str_replace($tokens, $replacements, (string) $this->content); + } + + /** + * @return string + */ + public function __toString() + { + try { + return $this->toString(); + } catch (\Exception $e) { + return sprintf('Error while rendering block: %s', $e->getMessage()); + } + } + + /** + * @param array $serialized + * @throws \RuntimeException + */ + public function build(array $serialized) + { + $this->checkVersion($serialized); + + $this->id = isset($serialized['id']) ? $serialized['id'] : $this->generateId(); + + if (isset($serialized['content'])) { + $this->setContent($serialized['content']); + } + + $blocks = isset($serialized['blocks']) ? (array) $serialized['blocks'] : []; + foreach ($blocks as $block) { + $this->addBlock(self::fromArray($block)); + } + } + + /** + * @param string $content + * @return $this + */ + public function setContent($content) + { + $this->content = $content; + + return $this; + } + + /** + * @param ContentBlockInterface $block + * @return $this + */ + public function addBlock(ContentBlockInterface $block) + { + $this->blocks[$block->getId()] = $block; + + return $this; + } + + /** + * @return string + */ + public function serialize() + { + return serialize($this->toArray()); + } + + /** + * @param string $serialized + */ + public function unserialize($serialized) + { + $array = unserialize($serialized); + $this->build($array); + } + + /** + * @return string + */ + protected function generateId() + { + return uniqid('', true); + } + + /** + * @param array $serialized + * @throws \RuntimeException + */ + protected function checkVersion(array $serialized) + { + $version = isset($serialized['_version']) ? (string) $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 new file mode 100644 index 000000000..4f2434c9d --- /dev/null +++ b/system/src/Grav/Framework/ContentBlock/ContentBlockInterface.php @@ -0,0 +1,75 @@ +getAssetsFast(); + + $this->sortAssets($assets['styles']); + $this->sortAssets($assets['scripts']); + $this->sortAssets($assets['html']); + + return $assets; + } + + /** + * @return array + */ + public function getFrameworks() + { + $assets = $this->getAssetsFast(); + + return array_keys($assets['frameworks']); + } + + /** + * @param string $location + * @return array + */ + public function getStyles($location = 'head') + { + return $this->getAssetsInLocation('styles', $location); + } + + /** + * @param string $location + * @return array + */ + public function getScripts($location = 'head') + { + return $this->getAssetsInLocation('scripts', $location); + } + + /** + * @param string $location + * @return array + */ + public function getHtml($location = 'bottom') + { + return $this->getAssetsInLocation('html', $location); + } + + /** + * @return array[] + */ + public function toArray() + { + $array = parent::toArray(); + + if ($this->frameworks) { + $array['frameworks'] = $this->frameworks; + } + if ($this->styles) { + $array['styles'] = $this->styles; + } + if ($this->scripts) { + $array['scripts'] = $this->scripts; + } + if ($this->html) { + $array['html'] = $this->html; + } + + return $array; + } + + /** + * @param array $serialized + * @throws \RuntimeException + */ + public function build(array $serialized) + { + parent::build($serialized); + + $this->frameworks = isset($serialized['frameworks']) ? (array) $serialized['frameworks'] : []; + $this->styles = isset($serialized['styles']) ? (array) $serialized['styles'] : []; + $this->scripts = isset($serialized['scripts']) ? (array) $serialized['scripts'] : []; + $this->html = isset($serialized['html']) ? (array) $serialized['html'] : []; + } + + /** + * @param string $framework + * @return $this + */ + public function addFramework($framework) + { + $this->frameworks[$framework] = 1; + + return $this; + } + + /** + * @param string|array $element + * @param int $priority + * @param string $location + * @return bool + * + * @example $block->addStyle('assets/js/my.js'); + * @example $block->addStyle(['href' => 'assets/js/my.js', 'media' => 'screen']); + */ + public function addStyle($element, $priority = 0, $location = 'head') + { + if (!is_array($element)) { + $element = ['href' => (string) $element]; + } + if (empty($element['href'])) { + return false; + } + if (!isset($this->styles[$location])) { + $this->styles[$location] = []; + } + + $id = !empty($element['id']) ? ['id' => (string) $element['id']] : []; + $href = $element['href']; + $type = !empty($element['type']) ? (string) $element['type'] : 'text/css'; + $media = !empty($element['media']) ? (string) $element['media'] : null; + unset( + $element['tag'], + $element['id'], + $element['rel'], + $element['content'], + $element['href'], + $element['type'], + $element['media'] + ); + + $this->styles[$location][md5($href) . sha1($href)] = [ + ':type' => 'file', + ':priority' => (int) $priority, + 'href' => $href, + 'type' => $type, + 'media' => $media, + 'element' => $element + ] + $id; + + return true; + } + + /** + * @param string|array $element + * @param int $priority + * @param string $location + * @return bool + */ + public function addInlineStyle($element, $priority = 0, $location = 'head') + { + if (!is_array($element)) { + $element = ['content' => (string) $element]; + } + if (empty($element['content'])) { + return false; + } + if (!isset($this->styles[$location])) { + $this->styles[$location] = []; + } + + $content = (string) $element['content']; + $type = !empty($element['type']) ? (string) $element['type'] : 'text/css'; + + $this->styles[$location][md5($content) . sha1($content)] = [ + ':type' => 'inline', + ':priority' => (int) $priority, + 'content' => $content, + 'type' => $type + ]; + + return true; + } + + /** + * @param string|array $element + * @param int $priority + * @param string $location + * @return bool + */ + public function addScript($element, $priority = 0, $location = 'head') + { + if (!is_array($element)) { + $element = ['src' => (string) $element]; + } + if (empty($element['src'])) { + return false; + } + if (!isset($this->scripts[$location])) { + $this->scripts[$location] = []; + } + + $src = $element['src']; + $type = !empty($element['type']) ? (string) $element['type'] : 'text/javascript'; + $defer = isset($element['defer']) ? true : false; + $async = isset($element['async']) ? true : false; + $handle = !empty($element['handle']) ? (string) $element['handle'] : ''; + + $this->scripts[$location][md5($src) . sha1($src)] = [ + ':type' => 'file', + ':priority' => (int) $priority, + 'src' => $src, + 'type' => $type, + 'defer' => $defer, + 'async' => $async, + 'handle' => $handle + ]; + + return true; + } + + /** + * @param string|array $element + * @param int $priority + * @param string $location + * @return bool + */ + public function addInlineScript($element, $priority = 0, $location = 'head') + { + if (!is_array($element)) { + $element = ['content' => (string) $element]; + } + if (empty($element['content'])) { + return false; + } + if (!isset($this->scripts[$location])) { + $this->scripts[$location] = []; + } + + $content = (string) $element['content']; + $type = !empty($element['type']) ? (string) $element['type'] : 'text/javascript'; + + $this->scripts[$location][md5($content) . sha1($content)] = [ + ':type' => 'inline', + ':priority' => (int) $priority, + 'content' => $content, + 'type' => $type + ]; + + return true; + } + + /** + * @param string $html + * @param int $priority + * @param string $location + * @return bool + */ + public function addHtml($html, $priority = 0, $location = 'bottom') + { + if (empty($html) || !is_string($html)) { + return false; + } + if (!isset($this->html[$location])) { + $this->html[$location] = []; + } + + $this->html[$location][md5($html) . sha1($html)] = [ + ':priority' => (int) $priority, + 'html' => $html + ]; + + return true; + } + + /** + * @return array + */ + protected function getAssetsFast() + { + $assets = [ + 'frameworks' => $this->frameworks, + 'styles' => $this->styles, + 'scripts' => $this->scripts, + 'html' => $this->html + ]; + + foreach ($this->blocks as $block) { + if ($block instanceof HtmlBlock) { + $blockAssets = $block->getAssetsFast(); + $assets['frameworks'] += $blockAssets['frameworks']; + + foreach ($blockAssets['styles'] as $location => $styles) { + if (!isset($assets['styles'][$location])) { + $assets['styles'][$location] = $styles; + } elseif ($styles) { + $assets['styles'][$location] += $styles; + } + } + + foreach ($blockAssets['scripts'] as $location => $scripts) { + if (!isset($assets['scripts'][$location])) { + $assets['scripts'][$location] = $scripts; + } elseif ($scripts) { + $assets['scripts'][$location] += $scripts; + } + } + + foreach ($blockAssets['html'] as $location => $htmls) { + if (!isset($assets['html'][$location])) { + $assets['html'][$location] = $htmls; + } elseif ($htmls) { + $assets['html'][$location] += $htmls; + } + } + } + } + + return $assets; + } + + /** + * @param string $type + * @param string $location + * @return array + */ + protected function getAssetsInLocation($type, $location) + { + $assets = $this->getAssetsFast(); + + if (empty($assets[$type][$location])) { + return []; + } + + $styles = $assets[$type][$location]; + $this->sortAssetsInLocation($styles); + + return $styles; + } + + /** + * @param array $items + */ + protected function sortAssetsInLocation(array &$items) + { + $count = 0; + foreach ($items as &$item) { + $item[':order'] = ++$count; + } + unset($item); + + uasort( + $items, + function ($a, $b) { + return ($a[':priority'] === $b[':priority']) + ? $a[':order'] - $b[':order'] : $a[':priority'] - $b[':priority']; + } + ); + } + + /** + * @param array $array + */ + protected function sortAssets(array &$array) + { + foreach ($array as $location => &$items) { + $this->sortAssetsInLocation($items); + } + } +} diff --git a/system/src/Grav/Framework/ContentBlock/HtmlBlockInterface.php b/system/src/Grav/Framework/ContentBlock/HtmlBlockInterface.php new file mode 100644 index 000000000..204e199e5 --- /dev/null +++ b/system/src/Grav/Framework/ContentBlock/HtmlBlockInterface.php @@ -0,0 +1,94 @@ +addStyle('assets/js/my.js'); + * @example $block->addStyle(['href' => 'assets/js/my.js', 'media' => 'screen']); + */ + public function addStyle($element, $priority = 0, $location = 'head'); + + /** + * @param string|array $element + * @param int $priority + * @param string $location + * @return bool + */ + public function addInlineStyle($element, $priority = 0, $location = 'head'); + + /** + * @param string|array $element + * @param int $priority + * @param string $location + * @return bool + */ + public function addScript($element, $priority = 0, $location = 'head'); + + + /** + * @param string|array $element + * @param int $priority + * @param string $location + * @return bool + */ + public function addInlineScript($element, $priority = 0, $location = 'head'); + + /** + * @param string $html + * @param int $priority + * @param string $location + * @return bool + */ + public function addHtml($html, $priority = 0, $location = 'bottom'); +} diff --git a/system/src/Grav/Framework/Object/Access/ArrayAccessTrait.php b/system/src/Grav/Framework/Object/Access/ArrayAccessTrait.php new file mode 100644 index 000000000..37e7328e3 --- /dev/null +++ b/system/src/Grav/Framework/Object/Access/ArrayAccessTrait.php @@ -0,0 +1,64 @@ +hasProperty($offset); + } + + /** + * Returns the value at specified offset. + * + * @param mixed $offset The offset to retrieve. + * @return mixed Can return all value types. + */ + public function offsetGet($offset) + { + return $this->getProperty($offset); + } + + /** + * Assigns a value to the specified offset. + * + * @param mixed $offset The offset to assign the value to. + * @param mixed $value The value to set. + */ + public function offsetSet($offset, $value) + { + $this->setProperty($offset, $value); + } + + /** + * Unsets an offset. + * + * @param mixed $offset The offset to unset. + */ + public function offsetUnset($offset) + { + $this->unsetProperty($offset); + } + + abstract public function hasProperty($property); + abstract public function getProperty($property, $default = null); + abstract public function setProperty($property, $value); + abstract public function unsetProperty($property); +} diff --git a/system/src/Grav/Framework/Object/Access/NestedArrayAccessTrait.php b/system/src/Grav/Framework/Object/Access/NestedArrayAccessTrait.php new file mode 100644 index 000000000..256a1cef7 --- /dev/null +++ b/system/src/Grav/Framework/Object/Access/NestedArrayAccessTrait.php @@ -0,0 +1,64 @@ +hasNestedProperty($offset); + } + + /** + * Returns the value at specified offset. + * + * @param mixed $offset The offset to retrieve. + * @return mixed Can return all value types. + */ + public function offsetGet($offset) + { + return $this->getNestedProperty($offset); + } + + /** + * Assigns a value to the specified offset. + * + * @param mixed $offset The offset to assign the value to. + * @param mixed $value The value to set. + */ + public function offsetSet($offset, $value) + { + $this->setNestedProperty($offset, $value); + } + + /** + * Unsets an offset. + * + * @param mixed $offset The offset to unset. + */ + public function offsetUnset($offset) + { + $this->unsetNestedProperty($offset); + } + + abstract public function hasNestedProperty($property, $separator = null); + abstract public function getNestedProperty($property, $default = null, $separator = null); + abstract public function setNestedProperty($property, $value, $separator = null); + abstract public function unsetNestedProperty($property, $separator = null); +} diff --git a/system/src/Grav/Framework/Object/Access/NestedPropertyCollectionTrait.php b/system/src/Grav/Framework/Object/Access/NestedPropertyCollectionTrait.php new file mode 100644 index 000000000..65b155da7 --- /dev/null +++ b/system/src/Grav/Framework/Object/Access/NestedPropertyCollectionTrait.php @@ -0,0 +1,124 @@ +getIterator() as $id => $element) { + $list[$id] = $element->hasNestedProperty($property, $separator); + } + + return $list; + } + + /** + * @param string $property Object property to be fetched. + * @param mixed $default Default value if not set. + * @param string $separator Separator, defaults to '.' + * @return array Key/Value pairs of the properties. + */ + public function getNestedProperty($property, $default = null, $separator = null) + { + $list = []; + + /** @var NestedObjectInterface $element */ + foreach ($this->getIterator() as $id => $element) { + $list[$id] = $element->getNestedProperty($property, $default, $separator); + } + + return $list; + } + + /** + * @param string $property Object property to be updated. + * @param string $value New value. + * @param string $separator Separator, defaults to '.' + * @return $this + */ + public function setNestedProperty($property, $value, $separator = null) + { + /** @var NestedObjectInterface $element */ + foreach ($this->getIterator() as $element) { + $element->setNestedProperty($property, $value, $separator); + } + + return $this; + } + + /** + * @param string $property Object property to be updated. + * @param string $separator Separator, defaults to '.' + * @return $this + */ + public function unsetNestedProperty($property, $separator = null) + { + /** @var NestedObjectInterface $element */ + foreach ($this->getIterator() as $element) { + $element->unsetNestedProperty($property, $separator); + } + + return $this; + } + + /** + * @param string $property Object property to be updated. + * @param string $default Default value. + * @param string $separator Separator, defaults to '.' + * @return $this + */ + public function defNestedProperty($property, $default, $separator = null) + { + /** @var NestedObjectInterface $element */ + foreach ($this->getIterator() as $element) { + $element->defNestedProperty($property, $default, $separator); + } + + return $this; + } + + /** + * Group items in the collection by a field. + * + * @param string $property Object property to be used to make groups. + * @param string $separator Separator, defaults to '.' + * @return array + */ + public function group($property, $separator = null) + { + $list = []; + + /** @var NestedObjectInterface $element */ + foreach ($this->getIterator() as $element) { + $list[(string) $element->getNestedProperty($property, null, $separator)][] = $element; + } + + return $list; + } + + /** + * @return \Traversable + */ + abstract public function getIterator(); +} diff --git a/system/src/Grav/Framework/Object/Access/NestedPropertyTrait.php b/system/src/Grav/Framework/Object/Access/NestedPropertyTrait.php new file mode 100644 index 000000000..e01f42590 --- /dev/null +++ b/system/src/Grav/Framework/Object/Access/NestedPropertyTrait.php @@ -0,0 +1,182 @@ +getNestedProperty($property, $test, $separator) !== $test; + } + + /** + * @param string $property Object property to be fetched. + * @param mixed $default Default value if property has not been set. + * @param string $separator Separator, defaults to '.' + * @return mixed Property value. + */ + public function getNestedProperty($property, $default = null, $separator = null) + { + $separator = $separator ?: '.'; + $path = explode($separator, $property); + $offset = array_shift($path); + + if (!$this->hasProperty($offset)) { + return $default; + } + + $current = $this->getProperty($offset); + + while ($path) { + // Get property of nested Object. + if ($current instanceof ObjectInterface) { + if (method_exists($current, 'getNestedProperty')) { + return $current->getNestedProperty(implode($separator, $path), $default, $separator); + } + return $current->getProperty(implode($separator, $path), $default); + } + + $offset = array_shift($path); + + if ((is_array($current) || is_a($current, 'ArrayAccess')) && isset($current[$offset])) { + $current = $current[$offset]; + } elseif (is_object($current) && isset($current->{$offset})) { + $current = $current->{$offset}; + } else { + return $default; + } + }; + + return $current; + } + + + /** + * @param string $property Object property to be updated. + * @param string $value New value. + * @param string $separator Separator, defaults to '.' + * @return $this + * @throws \RuntimeException + */ + public function setNestedProperty($property, $value, $separator = null) + { + $separator = $separator ?: '.'; + $path = explode($separator, $property); + $offset = array_shift($path); + + if (!$path) { + $this->setProperty($offset, $value); + + return $this; + } + + $current = &$this->doGetProperty($offset, null, true); + + while ($path) { + $offset = array_shift($path); + + // Handle arrays and scalars. + if ($current === null) { + $current = [$offset => []]; + } elseif (is_array($current)) { + if (!isset($current[$offset])) { + $current[$offset] = []; + } + } else { + throw new \RuntimeException('Cannot set nested property on non-array value'); + } + + $current = &$current[$offset]; + }; + + $current = $value; + + return $this; + } + + /** + * @param string $property Object property to be updated. + * @param string $separator Separator, defaults to '.' + * @return $this + * @throws \RuntimeException + */ + public function unsetNestedProperty($property, $separator = null) + { + $separator = $separator ?: '.'; + $path = explode($separator, $property); + $offset = array_shift($path); + + if (!$path) { + $this->unsetProperty($offset); + + return $this; + } + + $last = array_pop($path); + $current = &$this->doGetProperty($offset, null, true); + + while ($path) { + $offset = array_shift($path); + + // Handle arrays and scalars. + if ($current === null) { + return $this; + } + if (is_array($current)) { + if (!isset($current[$offset])) { + return $this; + } + } else { + throw new \RuntimeException('Cannot set nested property on non-array value'); + } + + $current = &$current[$offset]; + }; + + unset($current[$last]); + + return $this; + } + + /** + * @param string $property Object property to be updated. + * @param string $default Default value. + * @param string $separator Separator, defaults to '.' + * @return $this + * @throws \RuntimeException + */ + public function defNestedProperty($property, $default, $separator = null) + { + if (!$this->hasNestedProperty($property, $separator)) { + $this->setNestedProperty($property, $default, $separator); + } + + return $this; + } + + + abstract public function hasProperty($property); + abstract public function getProperty($property, $default = null); + abstract public function setProperty($property, $value); + abstract public function unsetProperty($property); + abstract protected function &doGetProperty($property, $default = null, $doCreate = false); +} diff --git a/system/src/Grav/Framework/Object/Access/OverloadedPropertyTrait.php b/system/src/Grav/Framework/Object/Access/OverloadedPropertyTrait.php new file mode 100644 index 000000000..1524bd6ab --- /dev/null +++ b/system/src/Grav/Framework/Object/Access/OverloadedPropertyTrait.php @@ -0,0 +1,64 @@ +hasProperty($offset); + } + + /** + * Returns the value at specified offset. + * + * @param mixed $offset The offset to retrieve. + * @return mixed Can return all value types. + */ + public function __get($offset) + { + return $this->getProperty($offset); + } + + /** + * Assigns a value to the specified offset. + * + * @param mixed $offset The offset to assign the value to. + * @param mixed $value The value to set. + */ + public function __set($offset, $value) + { + $this->setProperty($offset, $value); + } + + /** + * Magic method to unset the attribute + * + * @param mixed $offset The name value to unset + */ + public function __unset($offset) + { + $this->unsetProperty($offset); + } + + abstract public function hasProperty($property); + abstract public function getProperty($property, $default = null); + abstract public function setProperty($property, $value); + abstract public function unsetProperty($property); +} diff --git a/system/src/Grav/Framework/Object/ArrayObject.php b/system/src/Grav/Framework/Object/ArrayObject.php new file mode 100644 index 000000000..08041337f --- /dev/null +++ b/system/src/Grav/Framework/Object/ArrayObject.php @@ -0,0 +1,26 @@ +getIterator() as $key => $value) { + $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); + } + + /** + * @return array + */ + public function getObjectKeys() + { + return $this->call('getKey'); + } + + /** + * @param string $property Object property to be matched. + * @return array Key/Value pairs of the properties. + */ + public function doHasProperty($property) + { + $list = []; + + /** @var ObjectInterface $element */ + foreach ($this->getIterator() as $id => $element) { + $list[$id] = $element->hasProperty($property); + } + + return $list; + } + + /** + * @param string $property Object property to be fetched. + * @param mixed $default Default value if not set. + * @return array Key/Value pairs of the properties. + */ + public function doGetProperty($property, $default = null) + { + $list = []; + + /** @var ObjectInterface $element */ + foreach ($this->getIterator() as $id => $element) { + $list[$id] = $element->getProperty($property, $default); + } + + return $list; + } + + /** + * @param string $property Object property to be updated. + * @param string $value New value. + * @return $this + */ + public function doSetProperty($property, $value) + { + /** @var ObjectInterface $element */ + foreach ($this->getIterator() as $element) { + $element->setProperty($property, $value); + } + + return $this; + } + + /** + * @param string $property Object property to be updated. + * @return $this + */ + public function doUnsetProperty($property) + { + /** @var ObjectInterface $element */ + foreach ($this->getIterator() as $element) { + $element->unsetProperty($property); + } + + return $this; + } + + /** + * @param string $property Object property to be updated. + * @param string $default Default value. + * @return $this + */ + public function doDefProperty($property, $default) + { + /** @var ObjectInterface $element */ + foreach ($this->getIterator() as $element) { + $element->defProperty($property, $default); + } + + return $this; + } + + /** + * @param string $method Method name. + * @param array $arguments List of arguments passed to the function. + * @return array Return values. + */ + public function call($method, array $arguments = []) + { + $list = []; + + foreach ($this->getIterator() as $id => $element) { + $list[$id] = method_exists($element, $method) + ? call_user_func_array([$element, $method], $arguments) : null; + } + + return $list; + } + + /** + * Group items in the collection by a field and return them as associated array. + * + * @param string $property + * @return array + */ + public function group($property) + { + $list = []; + + /** @var ObjectInterface $element */ + foreach ($this->getIterator() as $element) { + $list[(string) $element->getProperty($property)][] = $element; + } + + return $list; + } + + /** + * Group items in the collection by a field and return them as associated array of collections. + * + * @param string $property + * @return static[] + */ + 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); + } + + $collections[$id] = $collection; + } + + return $collections; + } + + /** + * @return \Traversable + */ + abstract public function getIterator(); +} diff --git a/system/src/Grav/Framework/Object/Base/ObjectTrait.php b/system/src/Grav/Framework/Object/Base/ObjectTrait.php new file mode 100644 index 000000000..6522e8eb1 --- /dev/null +++ b/system/src/Grav/Framework/Object/Base/ObjectTrait.php @@ -0,0 +1,174 @@ +_key ?: $this->getType() . '@' . spl_object_hash($this); + } + + /** + * @param string $property Object property name. + * @return bool True if property has been defined (can be null). + */ + public function hasProperty($property) + { + return $this->doHasProperty($property); + } + + /** + * @param string $property Object property to be fetched. + * @param mixed $default Default value if property has not been set. + * @return mixed Property value. + */ + public function getProperty($property, $default = null) + { + return $this->doGetProperty($property, $default); + } + + /** + * @param string $property Object property to be updated. + * @param string $value New value. + * @return $this + */ + public function setProperty($property, $value) + { + $this->doSetProperty($property, $value); + + return $this; + } + + /** + * @param string $property Object property to be unset. + * @return $this + */ + public function unsetProperty($property) + { + $this->doUnsetProperty($property); + + return $this; + } + + /** + * @param string $property Object property to be defined. + * @param mixed $default Default value. + * @return $this + */ + public function defProperty($property, $default) + { + if (!$this->hasProperty($property)) { + $this->setProperty($property, $default); + } + + return $this; + } + + /** + * Implements Serializable interface. + * + * @return string + */ + public function serialize() + { + return serialize($this->jsonSerialize()); + } + + /** + * @param string $serialized + */ + public function unserialize($serialized) + { + $data = unserialize($serialized); + + if (method_exists($this, 'initObjectProperties')) { + $this->initObjectProperties(); + } + $this->doUnserialize($data); + } + + /** + * @param array $serialized + */ + protected function doUnserialize(array $serialized) + { + if (!isset($serialized['key'], $serialized['type'], $serialized['elements']) || $serialized['type'] !== $this->getType()) { + throw new \InvalidArgumentException("Cannot unserialize '{$this->getType()}': Bad data"); + } + + $this->setKey($serialized['key']); + $this->setElements($serialized['elements']); + } + + /** + * Implements JsonSerializable interface. + * + * @return array + */ + public function jsonSerialize() + { + return ['key' => $this->getKey(), 'type' => $this->getType(), 'elements' => $this->getElements()]; + } + + /** + * Returns a string representation of this object. + * + * @return string + */ + public function __toString() + { + return $this->getKey(); + } + + /** + * @param string $key + */ + protected function setKey($key) + { + $this->_key = (string) $key; + } + + abstract protected function doHasProperty($property); + abstract protected function &doGetProperty($property, $default = null, $doCreate = false); + abstract protected function doSetProperty($property, $value); + abstract protected function doUnsetProperty($property); + abstract protected function getElements(); + abstract protected function setElements(array $elements); +} diff --git a/system/src/Grav/Framework/Object/Interfaces/NestedObjectInterface.php b/system/src/Grav/Framework/Object/Interfaces/NestedObjectInterface.php new file mode 100644 index 000000000..3f8d784d9 --- /dev/null +++ b/system/src/Grav/Framework/Object/Interfaces/NestedObjectInterface.php @@ -0,0 +1,57 @@ +setElements($elements)); + + $this->setKey($key); + } + + protected function getElements() + { + return $this->toArray(); + } + + protected function setElements(array $elements) + { + return $elements; + } +} diff --git a/system/src/Grav/Framework/Object/Property/ArrayPropertyTrait.php b/system/src/Grav/Framework/Object/Property/ArrayPropertyTrait.php new file mode 100644 index 000000000..400b163c8 --- /dev/null +++ b/system/src/Grav/Framework/Object/Property/ArrayPropertyTrait.php @@ -0,0 +1,109 @@ +setElements($elements); + $this->setKey($key); + } + + /** + * @param string $property Object property name. + * @return bool True if property has been defined (can be null). + */ + protected function doHasProperty($property) + { + return array_key_exists($property, $this->_elements); + } + + /** + * @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. + */ + protected function &doGetProperty($property, $default = null, $doCreate = false) + { + if (!array_key_exists($property, $this->_elements)) { + if ($doCreate) { + $this->_elements[$property] = null; + } else { + return $default; + } + } + + return $this->_elements[$property]; + } + + /** + * @param string $property Object property to be updated. + * @param mixed $value New value. + */ + protected function doSetProperty($property, $value) + { + $this->_elements[$property] = $value; + } + + /** + * @param string $property Object property to be unset. + */ + protected function doUnsetProperty($property) + { + unset($this->_elements[$property]); + } + + /** + * @param string $property + * @param mixed|null $default + * @return mixed|null + */ + protected function getElement($property, $default = null) + { + return array_key_exists($property, $this->_elements) ? $this->_elements[$property] : $default; + } + + /** + * @return array + */ + protected function getElements() + { + return $this->_elements; + } + + /** + * @param array $elements + */ + protected function setElements(array $elements) + { + $this->_elements = $elements; + } + + abstract protected function setKey($key); +} diff --git a/system/src/Grav/Framework/Object/Property/LazyPropertyTrait.php b/system/src/Grav/Framework/Object/Property/LazyPropertyTrait.php new file mode 100644 index 000000000..28b9432cd --- /dev/null +++ b/system/src/Grav/Framework/Object/Property/LazyPropertyTrait.php @@ -0,0 +1,117 @@ +offsetLoad($offset, $value)` called first time object property gets accessed + * - `$this->offsetPrepare($offset, $value)` called on every object property set + * - `$this->offsetSerialize($offset, $value)` called when the raw or serialized object property value is needed + * + * @package Grav\Framework\Object\Property + */ +trait LazyPropertyTrait +{ + use ArrayPropertyTrait, ObjectPropertyTrait { + ObjectPropertyTrait::__construct insteadof ArrayPropertyTrait; + ArrayPropertyTrait::doHasProperty as hasArrayProperty; + ArrayPropertyTrait::doGetProperty as getArrayProperty; + ArrayPropertyTrait::doSetProperty as setArrayProperty; + ArrayPropertyTrait::doUnsetProperty as unsetArrayProperty; + ArrayPropertyTrait::getElement as getArrayElement; + ArrayPropertyTrait::getElements as getArrayElements; + ArrayPropertyTrait::setElements insteadof ObjectPropertyTrait; + ObjectPropertyTrait::doHasProperty as hasObjectProperty; + ObjectPropertyTrait::doGetProperty as getObjectProperty; + ObjectPropertyTrait::doSetProperty as setObjectProperty; + ObjectPropertyTrait::doUnsetProperty as unsetObjectProperty; + ObjectPropertyTrait::getElement as getObjectElement; + ObjectPropertyTrait::getElements as getObjectElements; + } + + /** + * @param string $property Object property name. + * @return bool True if property has been defined (can be null). + */ + protected function doHasProperty($property) + { + return $this->hasArrayProperty($property) || $this->hasObjectProperty($property); + } + + /** + * @param string $property Object property to be fetched. + * @param mixed $default Default value if property has not been set. + * @return mixed Property value. + */ + protected function &doGetProperty($property, $default = null, $doCreate = false) + { + if ($this->hasObjectProperty($property)) { + return $this->getObjectProperty($property, $default, function ($default = null) use ($property) { + return $this->getArrayProperty($property, $default); + }); + } + + return $this->getArrayProperty($property, $default, $doCreate); + } + + /** + * @param string $property Object property to be updated. + * @param mixed $value New value. + * @return $this + */ + protected function doSetProperty($property, $value) + { + if ($this->hasObjectProperty($property)) { + $this->setObjectProperty($property, $value); + } else { + $this->setArrayProperty($property, $value); + } + + return $this; + } + + /** + * @param string $property Object property to be unset. + * @return $this + */ + protected function doUnsetProperty($property) + { + $this->hasObjectProperty($property) ? + $this->unsetObjectProperty($property) : $this->unsetArrayProperty($property); + + return $this; + } + + /** + * @param string $property + * @param mixed|null $default + * @return mixed|null + */ + protected function getElement($property, $default = null) + { + if ($this->isPropertyLoaded($property)) { + return $this->getObjectElement($property, $default); + } + + return $this->getArrayElement($property, $default); + } + + /** + * @return array + */ + protected function getElements() + { + return $this->getObjectElements() + $this->getArrayElements(); + } +} diff --git a/system/src/Grav/Framework/Object/Property/MixedPropertyTrait.php b/system/src/Grav/Framework/Object/Property/MixedPropertyTrait.php new file mode 100644 index 000000000..2c86376bf --- /dev/null +++ b/system/src/Grav/Framework/Object/Property/MixedPropertyTrait.php @@ -0,0 +1,122 @@ +offsetLoad($offset, $value)` called first time object property gets accessed + * - `$this->offsetPrepare($offset, $value)` called on every object property set + * - `$this->offsetSerialize($offset, $value)` called when the raw or serialized object property value is needed + + * + * @package Grav\Framework\Object\Property + */ +trait MixedPropertyTrait +{ + use ArrayPropertyTrait, ObjectPropertyTrait { + ObjectPropertyTrait::__construct insteadof ArrayPropertyTrait; + ArrayPropertyTrait::doHasProperty as hasArrayProperty; + ArrayPropertyTrait::doGetProperty as getArrayProperty; + ArrayPropertyTrait::doSetProperty as setArrayProperty; + ArrayPropertyTrait::doUnsetProperty as unsetArrayProperty; + ArrayPropertyTrait::getElement as getArrayElement; + ArrayPropertyTrait::getElements as getArrayElements; + ArrayPropertyTrait::setElements as setArrayElements; + ObjectPropertyTrait::doHasProperty as hasObjectProperty; + ObjectPropertyTrait::doGetProperty as getObjectProperty; + ObjectPropertyTrait::doSetProperty as setObjectProperty; + ObjectPropertyTrait::doUnsetProperty as unsetObjectProperty; + ObjectPropertyTrait::getElement as getObjectElement; + ObjectPropertyTrait::getElements as getObjectElements; + ObjectPropertyTrait::setElements as setObjectElements; + } + + /** + * @param string $property Object property name. + * @return bool True if property has been defined (can be null). + */ + protected function doHasProperty($property) + { + return $this->hasArrayProperty($property) || $this->hasObjectProperty($property); + } + + /** + * @param string $property Object property to be fetched. + * @param mixed $default Default value if property has not been set. + * @return mixed Property value. + */ + protected function &doGetProperty($property, $default = null, $doCreate = false) + { + if ($this->hasObjectProperty($property)) { + return $this->getObjectProperty($property); + } + + return $this->getArrayProperty($property, $default, $doCreate); + } + + /** + * @param string $property Object property to be updated. + * @param mixed $value New value. + * @return $this + */ + protected function doSetProperty($property, $value) + { + $this->hasObjectProperty($property) + ? $this->setObjectProperty($property, $value) : $this->setArrayProperty($property, $value); + + return $this; + } + + /** + * @param string $property Object property to be unset. + * @return $this + */ + protected function doUnsetProperty($property) + { + $this->hasObjectProperty($property) ? + $this->unsetObjectProperty($property) : $this->unsetArrayProperty($property); + + return $this; + } + + /** + * @param string $property + * @param mixed|null $default + * @return mixed|null + */ + protected function getElement($property, $default = null) + { + if ($this->hasObjectProperty($property)) { + return $this->getObjectElement($property, $default); + } + + return $this->getArrayElement($property, $default); + } + + /** + * @return array + */ + protected function getElements() + { + return $this->getObjectElements() + $this->getArrayElements(); + } + + /** + * @param array $elements + */ + protected function setElements(array $elements) + { + $this->setObjectElements(array_intersect_key($elements, $this->_definedProperties)); + $this->setArrayElements(array_diff_key($elements, $this->_definedProperties)); + } +} diff --git a/system/src/Grav/Framework/Object/Property/ObjectPropertyTrait.php b/system/src/Grav/Framework/Object/Property/ObjectPropertyTrait.php new file mode 100644 index 000000000..c26903f21 --- /dev/null +++ b/system/src/Grav/Framework/Object/Property/ObjectPropertyTrait.php @@ -0,0 +1,203 @@ +offsetLoad($offset, $value)` called first time object property gets accessed + * - `$this->offsetPrepare($offset, $value)` called on every object property set + * - `$this->offsetSerialize($offset, $value)` called when the raw or serialized object property value is needed + * + * @package Grav\Framework\Object\Property + */ +trait ObjectPropertyTrait +{ + /** + * @var array + */ + private $_definedProperties; + + /** + * @param array $elements + * @param string $key + * @throws \InvalidArgumentException + */ + public function __construct(array $elements = [], $key = null) + { + $this->initObjectProperties(); + $this->setElements($elements); + $this->setKey($key); + } + + /** + * @param string $property Object property name. + * @return bool True if property has been loaded. + */ + protected function isPropertyLoaded($property) + { + return !empty($this->_definedProperties[$property]); + } + + /** + * @param string $offset + * @param mixed $value + * @return mixed + */ + protected function offsetLoad($offset, $value) + { + $methodName = "offsetLoad_{$offset}"; + + return method_exists($this, $methodName)? $this->{$methodName}($value) : $value; + } + + /** + * @param string $offset + * @param mixed $value + * @return mixed + */ + protected function offsetPrepare($offset, $value) + { + $methodName = "offsetPrepare_{$offset}"; + + return method_exists($this, $methodName) ? $this->{$methodName}($value) : $value; + } + + /** + * @param string $offset + * @param mixed $value + * @return mixed + */ + protected function offsetSerialize($offset, $value) + { + $methodName = "offsetSerialize_{$offset}"; + + return method_exists($this, $methodName) ? $this->{$methodName}($value) : $value; + } + + /** + * @param string $property Object property name. + * @return bool True if property has been defined (can be null). + */ + protected function doHasProperty($property) + { + return array_key_exists($property, $this->_definedProperties); + } + + /** + * @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. + */ + protected function &doGetProperty($property, $default = null, $doCreate = false) + { + if (!array_key_exists($property, $this->_definedProperties)) { + throw new \InvalidArgumentException("Property '{$property}' does not exist in the object!"); + } + + if (empty($this->_definedProperties[$property])) { + if ($doCreate === true) { + $this->_definedProperties[$property] = true; + $this->{$property} = null; + } elseif (is_callable($doCreate)) { + $this->_definedProperties[$property] = true; + $this->{$property} = $this->offsetLoad($property, $doCreate()); + } else { + return $default; + } + } + + return $this->{$property}; + } + + /** + * @param string $property Object property to be updated. + * @param mixed $value New value. + * @throws \InvalidArgumentException + */ + protected function doSetProperty($property, $value) + { + if (!array_key_exists($property, $this->_definedProperties)) { + throw new \InvalidArgumentException("Property '{$property}' does not exist in the object!"); + } + + $this->_definedProperties[$property] = true; + $this->{$property} = $this->offsetPrepare($property, $value); + } + + /** + * @param string $property Object property to be unset. + */ + protected function doUnsetProperty($property) + { + if (!array_key_exists($property, $this->_definedProperties)) { + return; + } + + $this->_definedProperties[$property] = false; + unset($this->{$property}); + } + + protected function initObjectProperties() + { + $this->_definedProperties = []; + foreach (get_object_vars($this) as $property => $value) { + if ($property[0] !== '_') { + $this->_definedProperties[$property] = ($value !== null); + } + } + } + + /** + * @param string $property + * @param mixed|null $default + * @return mixed|null + */ + protected function getElement($property, $default = null) + { + if (empty($this->_definedProperties[$property])) { + return $default; + } + + return $this->offsetSerialize($property, $this->{$property}); + } + + /** + * @return array + */ + protected function getElements() + { + $properties = array_intersect_key(get_object_vars($this), array_filter($this->_definedProperties)); + + $elements = []; + foreach ($properties as $offset => $value) { + $elements[$offset] = $this->offsetSerialize($offset, $value); + } + + return $elements; + } + + /** + * @param array $elements + */ + protected function setElements(array $elements) + { + foreach ($elements as $property => $value) { + $this->setProperty($property, $value); + } + } + + abstract public function setProperty($property, $value); + abstract protected function setKey($key); +} diff --git a/system/src/Grav/Framework/Object/PropertyObject.php b/system/src/Grav/Framework/Object/PropertyObject.php new file mode 100644 index 000000000..99f7e7ff9 --- /dev/null +++ b/system/src/Grav/Framework/Object/PropertyObject.php @@ -0,0 +1,26 @@ + 80, + 'https' => 443 + ]; + + /** @var string Uri scheme. */ + private $scheme = ''; + + /** @var string Uri user. */ + private $user = ''; + + /** @var string Uri password. */ + private $password = ''; + + /** @var string Uri host. */ + private $host = ''; + + /** @var int|null Uri port. */ + private $port; + + /** @var string Uri path. */ + private $path = ''; + + /** @var string Uri query string (without ?). */ + private $query = ''; + + /** @var string Uri fragment (without #). */ + private $fragment = ''; + + /** + * Please define constructor which calls $this->init(). + */ + abstract public function __construct(); + + /** + * @inheritdoc + */ + public function getScheme() + { + return $this->scheme; + } + + /** + * @inheritdoc + */ + public function getAuthority() + { + $authority = $this->host; + + $userInfo = $this->getUserInfo(); + if ($userInfo !== '') { + $authority = $userInfo . '@' . $authority; + } + + if ($this->port !== null) { + $authority .= ':' . $this->port; + } + + return $authority; + } + + /** + * @inheritdoc + */ + public function getUserInfo() + { + $userInfo = $this->user; + + if ($this->password !== '') { + $userInfo .= ':' . $this->password; + } + + return $userInfo; + } + + /** + * @inheritdoc + */ + public function getHost() + { + return $this->host; + } + + /** + * @inheritdoc + */ + public function getPort() + { + return $this->port; + } + + /** + * @inheritdoc + */ + public function getPath() + { + return $this->path; + } + + /** + * @inheritdoc + */ + public function getQuery() + { + return $this->query; + } + + /** + * @inheritdoc + */ + public function getFragment() + { + return $this->fragment; + } + + /** + * @inheritdoc + */ + public function withScheme($scheme) + { + $scheme = UriPartsFilter::filterScheme($scheme); + + if ($this->scheme === $scheme) { + return $this; + } + + $new = clone $this; + $new->scheme = $scheme; + $new->unsetDefaultPort(); + $new->validate(); + + return $new; + } + + /** + * @inheritdoc + * @throws \InvalidArgumentException + */ + public function withUserInfo($user, $password = '') + { + $user = UriPartsFilter::filterUserInfo($user); + $password = UriPartsFilter::filterUserInfo($password); + + if ($this->user === $user && $this->password === $password) { + return $this; + } + + $new = clone $this; + $new->user = $user; + $new->password = $user !== '' ? $password : ''; + $new->validate(); + + return $new; + } + + /** + * @inheritdoc + */ + public function withHost($host) + { + $host = UriPartsFilter::filterHost($host); + + if ($this->host === $host) { + return $this; + } + + $new = clone $this; + $new->host = $host; + $new->validate(); + + return $new; + } + + /** + * @inheritdoc + */ + public function withPort($port) + { + $port = UriPartsFilter::filterPort($port); + + if ($this->port === $port) { + return $this; + } + + $new = clone $this; + $new->port = $port; + $new->unsetDefaultPort(); + $new->validate(); + + return $new; + } + + /** + * @inheritdoc + */ + public function withPath($path) + { + $path = UriPartsFilter::filterPath($path); + + if ($this->path === $path) { + return $this; + } + + $new = clone $this; + $new->path = $path; + $new->validate(); + + return $new; + } + + /** + * @inheritdoc + */ + public function withQuery($query) + { + $query = UriPartsFilter::filterQueryOrFragment($query); + + if ($this->query === $query) { + return $this; + } + + $new = clone $this; + $new->query = $query; + + return $new; + } + + /** + * @inheritdoc + * @throws \InvalidArgumentException + */ + public function withFragment($fragment) + { + $fragment = UriPartsFilter::filterQueryOrFragment($fragment); + + if ($this->fragment === $fragment) { + return $this; + } + + $new = clone $this; + $new->fragment = $fragment; + + return $new; + } + + /** + * @return string + */ + public function __toString() + { + return $this->getUrl(); + } + + /** + * @return array + */ + protected function getParts() + { + return [ + 'scheme' => $this->scheme, + 'host' => $this->host, + 'port' => $this->port, + 'user' => $this->user, + 'pass' => $this->password, + 'path' => $this->path, + 'query' => $this->query, + 'fragment' => $this->fragment + ]; + } + + /** + * Return the fully qualified base URL ( like http://getgrav.org ). + * + * Note that this method never includes a trailing / + * + * @return string + */ + protected function getBaseUrl() + { + $uri = ''; + + $scheme = $this->getScheme(); + if ($scheme !== '') { + $uri .= $scheme . ':'; + } + + $authority = $this->getAuthority(); + if ($authority !== '' || $scheme === 'file') { + $uri .= '//' . $authority; + } + + return $uri; + } + + /** + * @return string + */ + protected function getUrl() + { + $uri = $this->getBaseUrl() . $this->getPath(); + + $query = $this->getQuery(); + if ($query !== '') { + $uri .= '?' . $query; + } + + $fragment = $this->getFragment(); + if ($fragment !== '') { + $uri .= '#' . $fragment; + } + + return $uri; + } + + /** + * @return string + */ + protected function getUser() + { + return $this->user; + } + + /** + * @return string + */ + protected function getPassword() + { + return $this->password; + } + + /** + * @param array $parts + * @throws \InvalidArgumentException + */ + protected function initParts(array $parts) + { + $this->scheme = isset($parts['scheme']) ? UriPartsFilter::filterScheme($parts['scheme']) : ''; + $this->user = isset($parts['user']) ? UriPartsFilter::filterUserInfo($parts['user']) : ''; + $this->password = isset($parts['pass']) ? UriPartsFilter::filterUserInfo($parts['pass']) : ''; + $this->host = isset($parts['host']) ? UriPartsFilter::filterHost($parts['host']) : ''; + $this->port = isset($parts['port']) ? UriPartsFilter::filterPort((int)$parts['port']) : null; + $this->path = isset($parts['path']) ? UriPartsFilter::filterPath($parts['path']) : ''; + $this->query = isset($parts['query']) ? UriPartsFilter::filterQueryOrFragment($parts['query']) : ''; + $this->fragment = isset($parts['fragment']) ? UriPartsFilter::filterQueryOrFragment($parts['fragment']) : ''; + + $this->unsetDefaultPort(); + $this->validate(); + } + + /** + * @throws \InvalidArgumentException + */ + private function validate() + { + if ($this->host === '' && ($this->scheme === 'http' || $this->scheme === 'https')) { + throw new \InvalidArgumentException('Uri with a scheme must have a host'); + } + + if ($this->getAuthority() === '') { + if (0 === strpos($this->path, '//')) { + throw new \InvalidArgumentException('The path of a URI without an authority must not start with two slashes \'//\''); + } + if ($this->scheme === '' && false !== strpos(explode('/', $this->path, 2)[0], ':')) { + throw new \InvalidArgumentException('A relative URI must not have a path beginning with a segment containing a colon'); + } + } elseif (isset($this->path[0]) && $this->path[0] !== '/') { + throw new \InvalidArgumentException('The path of a URI with an authority must start with a slash \'/\' or be empty'); + } + } + + protected function isDefaultPort() + { + $scheme = $this->scheme; + $port = $this->port; + + return $this->port === null + || (isset(static::$defaultPorts[$scheme]) && $port === static::$defaultPorts[$scheme]); + } + + private function unsetDefaultPort() + { + if ($this->isDefaultPort()) { + $this->port = null; + } + } +} diff --git a/system/src/Grav/Framework/Route/Route.php b/system/src/Grav/Framework/Route/Route.php new file mode 100644 index 000000000..7ab2821b0 --- /dev/null +++ b/system/src/Grav/Framework/Route/Route.php @@ -0,0 +1,279 @@ +initParts($parts); + } + + /** + * @return array + */ + public function getParts() + { + return [ + 'path' => $this->getUriPath(), + 'query' => $this->getUriQuery(), + 'grav' => [ + 'root' => $this->root, + 'language' => $this->language, + 'route' => $this->route, + 'grav_params' => $this->gravParams, + 'query_params' => $this->queryParams, + ], + ]; + } + + /** + * @return string + */ + public function getRootPrefix() + { + return $this->root; + } + + /** + * @return string + */ + public function getLanguagePrefix() + { + return $this->language !== '' ? '/' . $this->language : ''; + } + + /** + * @return string + */ + public function getRoute() + { + return '/' . $this->route; + } + + /** + * Return array of both query and Grav parameters. + * + * If a parameter exists in both, prefer Grav parameter. + * + * @return array + */ + public function getParams() + { + return $this->gravParams + $this->queryParams; + } + + /** + * @return array + */ + public function getGravParams() + { + return $this->gravParams; + } + + /** + * @return array + */ + public function getQueryParams() + { + return $this->queryParams; + } + + /** + * Return value of the parameter, looking into both Grav parameters and query parameters. + * + * If the parameter exists in both, return Grav parameter. + * + * @param string $param + * @return string|null + */ + public function getParam($param) + { + $value = $this->getGravParam($param); + if ($value === null) { + $value = $this->getQueryParam($param); + } + + return $value; + } + + /** + * @param string $param + * @return string|null + */ + public function getGravParam($param) + { + return isset($this->gravParams[$param]) ? $this->gravParams[$param] : null; + } + + /** + * @param string $param + * @return string|null + */ + public function getQueryParam($param) + { + return isset($this->queryParams[$param]) ? $this->queryParams[$param] : null; + } + + /** + * @param string $param + * @param mixed $value + * @return Route + */ + public function withGravParam($param, $value) + { + return $this->withParam('gravParams', $param, $value); + } + + /** + * @param string $param + * @param mixed $value + * @return Route + */ + public function withQueryParam($param, $value) + { + return $this->withParam('queryParams', $param, $value); + } + + /** + * @return \Grav\Framework\Uri\Uri + */ + public function getUri() + { + return UriFactory::createFromParts($this->getParts()); + } + + /** + * @return string + */ + public function __toString() + { + $url = $this->getUriPath(); + + if ($this->queryParams) { + $url .= '?' . $this->getUriQuery(); + } + + return $url; + } + + /** + * @param string $type + * @param string $param + * @param mixed $value + * @return static + */ + protected function withParam($type, $param, $value) + { + $oldValue = isset($this->{$type}[$param]) ? $this->{$type}[$param] : null; + $newValue = null !== $value ? (string)$value : null; + + if ($oldValue === $newValue) { + return $this; + } + + $new = clone $this; + if ($newValue === null) { + unset($new->{$type}[$param]); + } else { + $new->{$type}[$param] = $newValue; + } + + return $new; + } + + /** + * @return string + */ + protected function getUriPath() + { + $parts = [$this->root]; + + if ($this->language !== '') { + $parts[] = $this->language; + } + + if ($this->route !== '') { + $parts[] = $this->route; + } + + if ($this->gravParams) { + $parts[] = RouteFactory::buildParams($this->gravParams); + } + + return implode('/', $parts); + } + + /** + * @return string + */ + protected function getUriQuery() + { + return UriFactory::buildQuery($this->queryParams); + } + + /** + * @param array $parts + */ + protected function initParts(array $parts) + { + if (isset($parts['grav'])) { + $gravParts = $parts['grav']; + $this->root = $gravParts['root']; + $this->language = $gravParts['language']; + $this->route = $gravParts['route']; + $this->gravParams = $gravParts['params']; + $this->queryParams = $parts['query_params']; + + } else { + $this->root = RouteFactory::getRoot(); + $this->language = RouteFactory::getLanguage(); + + $path = isset($parts['path']) ? $parts['path'] : '/'; + if (isset($parts['params'])) { + $this->route = trim(rawurldecode($path), '/'); + $this->gravParams = $parts['params']; + } else { + $this->route = trim(RouteFactory::stripParams($path, true), '/'); + $this->gravParams = RouteFactory::getParams($path); + } + if (isset($parts['query'])) { + $this->queryParams = UriFactory::parseQuery($parts['query']); + } + } + } +} diff --git a/system/src/Grav/Framework/Route/RouteFactory.php b/system/src/Grav/Framework/Route/RouteFactory.php new file mode 100644 index 000000000..a406926cf --- /dev/null +++ b/system/src/Grav/Framework/Route/RouteFactory.php @@ -0,0 +1,131 @@ + $value) { + $output[] = "{$key}{$delimiter}{$value}"; + } + + return implode('/', $output); + } + + /** + * @param string $path + * @param bool $decode + * @return string + */ + public static function stripParams($path, $decode = false) + { + $pos = strpos($path, self::$delimiter); + + if ($pos === false) { + return $path; + } + + $path = dirname(substr($path, 0, $pos)); + if ($path === '.') { + return ''; + } + + return $decode ? rawurldecode($path) : $path; + } + + /** + * @param string $path + * @return array + */ + public static function getParams($path) + { + $params = ltrim(substr($path, strlen(static::stripParams($path))), '/'); + + return $params !== '' ? static::parseParams($params) : []; + } + + /** + * @param string $str + * @return array + */ + public static function parseParams($str) + { + $delimiter = self::$delimiter; + + $params = explode('/', $str); + foreach ($params as &$param) { + $parts = explode($delimiter, $param, 2); + if (isset($parts[1])) { + $param[rawurldecode($parts[0])] = rawurldecode($parts[1]); + } + } + + return $params; + } +} diff --git a/system/src/Grav/Framework/Uri/Uri.php b/system/src/Grav/Framework/Uri/Uri.php new file mode 100644 index 000000000..2821360af --- /dev/null +++ b/system/src/Grav/Framework/Uri/Uri.php @@ -0,0 +1,213 @@ +initParts($parts); + } + + /** + * @return string + */ + public function getUser() + { + return parent::getUser(); + } + + /** + * @return string + */ + public function getPassword() + { + return parent::getPassword(); + } + + /** + * @return array + */ + public function getParts() + { + return parent::getParts(); + } + + /** + * @return string + */ + public function getUrl() + { + return parent::getUrl(); + } + + /** + * @return string + */ + public function getBaseUrl() + { + return parent::getBaseUrl(); + } + + /** + * @param string $key + * @return string|null + */ + public function getQueryParam($key) + { + $queryParams = $this->getQueryParams(); + + return isset($queryParams[$key]) ? $queryParams[$key] : null; + } + + /** + * @param string $key + * @return UriInterface + */ + public function withoutQueryParam($key) + { + return GuzzleUri::withoutQueryValue($this, $key); + } + + /** + * @param string $key + * @param string|null $value + * @return UriInterface + */ + public function withQueryParam($key, $value) + { + return GuzzleUri::withQueryValue($this, $key, $value); + } + + /** + * @return array + */ + public function getQueryParams() + { + if ($this->queryParams === null) { + $this->queryParams = UriFactory::parseQuery($this->getQuery()); + } + + return $this->queryParams; + } + + /** + * @param array $params + * @return UriInterface + */ + public function withQueryParams(array $params) + { + $query = UriFactory::buildQuery($params); + + return $this->withQuery($query); + } + + /** + * Whether the URI has the default port of the current scheme. + * + * `$uri->getPort()` may return the standard port. This method can be used for some non-http/https Uri. + * + * @return bool + */ + public function isDefaultPort() + { + return $this->getPort() === null || GuzzleUri::isDefaultPort($this); + } + + /** + * Whether the URI is absolute, i.e. it has a scheme. + * + * An instance of UriInterface can either be an absolute URI or a relative reference. This method returns true + * if it is the former. An absolute URI has a scheme. A relative reference is used to express a URI relative + * to another URI, the base URI. Relative references can be divided into several forms: + * - network-path references, e.g. '//example.com/path' + * - absolute-path references, e.g. '/path' + * - relative-path references, e.g. 'subpath' + * + * @return bool + * @link https://tools.ietf.org/html/rfc3986#section-4 + */ + public function isAbsolute() + { + return GuzzleUri::isAbsolute($this); + } + + /** + * Whether the URI is a network-path reference. + * + * A relative reference that begins with two slash characters is termed an network-path reference. + * + * @return bool + * @link https://tools.ietf.org/html/rfc3986#section-4.2 + */ + public function isNetworkPathReference() + { + return GuzzleUri::isNetworkPathReference($this); + } + + /** + * Whether the URI is a absolute-path reference. + * + * A relative reference that begins with a single slash character is termed an absolute-path reference. + * + * @return bool + * @link https://tools.ietf.org/html/rfc3986#section-4.2 + */ + public function isAbsolutePathReference() + { + return GuzzleUri::isAbsolutePathReference($this); + } + + /** + * Whether the URI is a relative-path reference. + * + * A relative reference that does not begin with a slash character is termed a relative-path reference. + * + * @return bool + * @link https://tools.ietf.org/html/rfc3986#section-4.2 + */ + public function isRelativePathReference() + { + return GuzzleUri::isRelativePathReference($this); + } + + /** + * Whether the URI is a same-document reference. + * + * A same-document reference refers to a URI that is, aside from its fragment + * component, identical to the base URI. When no base URI is given, only an empty + * URI reference (apart from its fragment) is considered a same-document reference. + * + * @param UriInterface|null $base An optional base URI to compare against + * @return bool + * @link https://tools.ietf.org/html/rfc3986#section-4.4 + */ + public function isSameDocumentReference(UriInterface $base = null) + { + return GuzzleUri::isSameDocumentReference($this, $base); + } +} diff --git a/system/src/Grav/Framework/Uri/UriFactory.php b/system/src/Grav/Framework/Uri/UriFactory.php new file mode 100644 index 000000000..c6bd4289d --- /dev/null +++ b/system/src/Grav/Framework/Uri/UriFactory.php @@ -0,0 +1,159 @@ + $scheme, + 'user' => $user, + 'pass' => $pass, + 'host' => $host, + 'port' => $port, + 'path' => $path, + 'query' => $query + ]; + } + + /** + * UTF-8 aware parse_url() implementation. + * + * @param string $url + * @return array + * @throws \InvalidArgumentException + */ + public static function parseUrl($url) + { + if (!is_string($url)) { + throw new \InvalidArgumentException('URL must be a string'); + } + + $encodedUrl = preg_replace_callback( + '%[^:/@?&=#]+%u', + function ($matches) { return rawurlencode($matches[0]); }, + $url + ); + + $parts = parse_url($encodedUrl); + if ($parts === false) { + throw new \InvalidArgumentException('Malformed URL: ' . $encodedUrl); + } + + return $parts; + } + + /** + * Parse query string and return it as an array. + * + * @param string $query + * @return mixed + */ + public static function parseQuery($query) + { + parse_str($query, $params); + + return $params; + } + + /** + * Build query string from variables. + * + * @param array $params + * @return string + */ + public static function buildQuery(array $params) + { + return $params ? http_build_query($params, null, ini_get('arg_separator.output'), PHP_QUERY_RFC3986) : ''; + } +} diff --git a/system/src/Grav/Framework/Uri/UriPartsFilter.php b/system/src/Grav/Framework/Uri/UriPartsFilter.php new file mode 100644 index 000000000..36efa16cb --- /dev/null +++ b/system/src/Grav/Framework/Uri/UriPartsFilter.php @@ -0,0 +1,140 @@ += 1 && $port <= 65535))) { + return $port; + } + + throw new \InvalidArgumentException('Uri port must be null or an integer between 1 and 65535'); + } + + /** + * Filter Uri path. + * + * This method percent-encodes all reserved characters in the provided path string. This method + * will NOT double-encode characters that are already percent-encoded. + * + * @param string $path The raw uri path. + * @return string The RFC 3986 percent-encoded uri path. + * @throws \InvalidArgumentException If the path is invalid. + * @link http://www.faqs.org/rfcs/rfc3986.html + */ + public static function filterPath($path) + { + if (!is_string($path)) { + throw new \InvalidArgumentException('Uri path must be a string'); + } + + return preg_replace_callback( + '/(?:[^a-zA-Z0-9_\-\.~:@&=\+\$,\/;%]+|%(?![A-Fa-f0-9]{2}))/u', + function ($match) { + return rawurlencode($match[0]); + }, + $path + ); + } + + /** + * Filters the query string or fragment of a URI. + * + * @param string $query The raw uri query string. + * @return string The percent-encoded query string. + * @throws \InvalidArgumentException If the query is invalid. + */ + public static function filterQueryOrFragment($query) + { + if (!is_string($query)) { + throw new \InvalidArgumentException('Uri query string and fragment must be a string'); + } + + return preg_replace_callback( + '/(?:[^a-zA-Z0-9_\-\.~!\$&\'\(\)\*\+,;=%:@\/\?]+|%(?![A-Fa-f0-9]{2}))/u', + function ($match) { + return rawurlencode($match[0]); + }, + $query + ); + } +} diff --git a/user/config/system.yaml b/user/config/system.yaml index 474d8edfd..db6bc364b 100644 --- a/user/config/system.yaml +++ b/user/config/system.yaml @@ -4,7 +4,7 @@ home: alias: '/home' pages: - theme: antimatter + theme: quark markdown: extra: false process: @@ -41,5 +41,5 @@ debugger: shutdown: close_connection: true gpm: - releases: stable + releases: testing verify_peer: true diff --git a/user/pages/01.home/default.md b/user/pages/01.home/default.md index 3ebb8dda0..a8119bde9 100644 --- a/user/pages/01.home/default.md +++ b/user/pages/01.home/default.md @@ -1,13 +1,14 @@ --- title: Home +body_classes: title-center title-h1h2 --- -# Grav is Running! -## You have installed **Grav** successfully +# Say Hello to Grav! +## installation successful... -Congratulations! You have installed the **Base Grav Package** that provides a **simple page** and the default **antimatter** theme to get you started. +Congratulations! You have installed the **Base Grav Package** that provides a **simple page** and the default **Quark** theme to get you started. -!!! If you want a more **full-featured** base install, you should check out [**Skeleton** packages available in the downloads](http://getgrav.org/downloads). +!! If you see a **404 Error** when you click `Typography` in the menu, please refer to the [troubleshooting guide](http://learn.getgrav.org/troubleshooting/page-not-found). ### Find out all about Grav @@ -15,6 +16,8 @@ Congratulations! You have installed the **Base Grav Package** that provides a ** * Download **plugins**, **themes**, as well as other Grav **skeleton** packages from the [Grav Downloads](http://getgrav.org/downloads) page. * Check out our [Grav Development Blog](http://getgrav.org/blog) to find out the latest goings on in the Grav-verse. +!!! If you want a more **full-featured** base install, you should check out [**Skeleton** packages available in the downloads](http://getgrav.org/downloads). + ### Edit this Page To edit this page, simply navigate to the folder you installed **Grav** into, and then browse to the `user/pages/01.home` folder and open the `default.md` file in your [editor of choice](http://learn.getgrav.org/basics/requirements). You will see the content of this page in [Markdown format](http://learn.getgrav.org/content/markdown). @@ -23,7 +26,7 @@ To edit this page, simply navigate to the folder you installed **Grav** into, an Creating a new page is a simple affair in **Grav**. Simply follow these simple steps: -1. Navigate to your pages folder: `user/pages/` and create a new folder. In this example, we will use [explicit default ordering](http://learn.getgrav.org/content/content-pages) and call the folder `02.mypage`. +1. Navigate to your pages folder: `user/pages/` and create a new folder. In this example, we will use [explicit default ordering](http://learn.getgrav.org/content/content-pages) and call the folder `03.mypage`. 2. Launch your text editor and paste in the following sample code: --- @@ -33,7 +36,7 @@ Creating a new page is a simple affair in **Grav**. Simply follow these simple This is the body of **my new page** and I can easily use _Markdown_ syntax here. -3. Save this file in the `user/pages/02.mypage/` folder as `default.md`. This will tell **Grav** to render the page using the **default** template. +3. Save this file in the `user/pages/03.mypage/` folder as `default.md`. This will tell **Grav** to render the page using the **default** template. 4. That is it! Reload your browser to see your new page in the menu. ! NOTE: The page will automatically show up in the Menu after the "Home" menu item. If you wish to change the name that shows up in the Menu, simple add: `menu: My Page` between the dashes in the page content. This is called the YAML front matter, and it is where you configure page-specific options. diff --git a/user/pages/02.typography/default.md b/user/pages/02.typography/default.md new file mode 100644 index 000000000..30a5d1041 --- /dev/null +++ b/user/pages/02.typography/default.md @@ -0,0 +1,155 @@ +--- +title: Typography +--- + +! Details on the full capabiltiies of Spectre.css can be found in the [Official Spectre Documentation](https://picturepan2.github.io/spectre/elements.html) + +The [Quark theme](https://github.com/getgrav/grav-theme-quark) is the new default theme for Grav built with [Spectre.css](https://picturepan2.github.io/spectre/) the lightweight, responsive and modern CSS framework. Spectre provides basic styles for typography, elements, and a responsive layout system that utilizes best practices and consistent language design. + +### Headings + +# H1 Heading `40px` + +## H2 Heading `32px` + +### H3 Heading `28px` + +#### H4 Heading `24px` + +##### H5 Heading `20px` + +###### H6 Heading `16px` + +```html +# H1 Heading +# H1 Heading `40px`` + +H1 Heading +``` + +### Paragraphs + +Lorem ipsum dolor sit amet, consectetur [adipiscing elit. Praesent risus leo, dictum in vehicula sit amet](#), feugiat tempus tellus. Duis quis sodales risus. Etiam euismod ornare consequat. + +Climb leg rub face on everything give attitude nap all day for under the bed. Chase mice attack feet but rub face on everything hopped up on goofballs. + +### Markdown Semantic Text Elements + +**Bold** `**Bold**` + +_Italic_ `_Italic_` + +~~Deleted~~ `~~Deleted~~` + +`Inline Code` `` `Inline Code` `` + +### HTML Semantic Text Elements + +I18N `` + +Citation `` + +Ctrl + S `` + +TextSuperscripted `` + +TextSubscxripted `` + +Underlined `` + +Highlighted `` + + `